Search code examples
sql-server-2008sqlxml

CASE inside SELECT..FOR XML - avoiding multiple elements


How to avoid multiple <Debtor> and <Creditor> elements that are created. Below is a query I need to improve. Basically I need to get both <name> and <address> inside either <Creditor> or <Debtor> depending on Amt expressed by CASE condition.

SELECT
(SELECT  [Amt/@Curr] = t.Curr
        ,t.Amt 
        ,[EntryDetails/TxnDetails/PostClassDt] = CASE WHEN Amt < 0 THEN 'debit' END
        ,[EntryDetails/TxnDetails/PostClassCr] = CASE WHEN Amt >= 0 THEN 'return' END
        ,[EntryDetails/TxnDetails/Parties/Creditor/Name] = CASE WHEN Amt < 0 THEN Contractor END
        ,[EntryDetails/TxnDetails/Parties/Debtor/Name] = CASE WHEN Amt >= 0 THEN Contractor END
        ,[EntryDetails/TxnDetails/Parties/Creditor/Address] = CASE WHEN Amt < 0 THEN [Address] END
        ,[EntryDetails/TxnDetails/Parties/Debtor/Address] = CASE WHEN Amt >= 0 THEN [Address] END
FROM    (VALUES 
            ('EUR', -123.45, 'John Doe','St.John'),
            ('USD', 456.78, 'Jane Doe', 'St.Jane')
        ) t (Curr, Amt, Contractor, [Address])
FOR XML PATH('Entry'), TYPE
)   [Statement]
FOR XML PATH('Schema'),  ROOT('Document')

Required formatting shown below. Anyone help, please ?

<Document>
      <Schema>
        <Statement>
          <Entry>
            <Amt Curr="EUR">-123.45</Amt>
            <EntryDetails>
              <TxnDetails>
                <PostClassDt>debit</PostClassDt>
                <Parties>
                  <Creditor>
                    <Name>John Doe</Name>
                    <Address>St.John</Address>
                  </Creditor>
                </Parties>
              </TxnDetails>
            </EntryDetails>
          </Entry>
          <Entry>
            <Amt Curr="USD">456.78</Amt>
            <EntryDetails>
              <TxnDetails>
                <PostClassCr>return</PostClassCr>
                <Parties>
                  <Debtor>
                    <Name>Jane Doe</Name>
                    <Address>St.Jane</Address>
                  </Debtor>
                </Parties>
              </TxnDetails>
            </EntryDetails>
          </Entry>
        </Statement>
      </Schema>
    </Document>

Solution

  • Change the order of your select:

        SELECT
    (SELECT  [Amt/@Curr] = t.Curr
            ,t.Amt 
            ,[EntryDetails/TxnDetails/PostClassDt] = CASE WHEN Amt < 0 THEN 'debit' END
            ,[EntryDetails/TxnDetails/PostClassCr] = CASE WHEN Amt >= 0 THEN 'return' END
            ,[EntryDetails/TxnDetails/Parties/Creditor/Name] = CASE WHEN Amt < 0 THEN Contractor END
            ,[EntryDetails/TxnDetails/Parties/Creditor/Address] = CASE WHEN Amt < 0 THEN [Address] END
            ,[EntryDetails/TxnDetails/Parties/Debtor/Name] = CASE WHEN Amt >= 0 THEN Contractor END
            ,[EntryDetails/TxnDetails/Parties/Debtor/Address] = CASE WHEN Amt >= 0 THEN [Address] END
    FROM    (VALUES 
                ('EUR', -123.45, 'John Doe','St.John'),
                ('USD', 456.78, 'Jane Doe', 'St.Jane')
            ) t (Curr, Amt, Contractor, [Address])
    FOR XML PATH('Entry'), TYPE
    )   [Statement]
    FOR XML PATH('Schema'),  ROOT('Document')