Search code examples
sqlxmlnestedsubqueryfor-xml-path

How do I architect my SQL FOR XML PATH queries/sub-queries to represent my data?


Been banging my head for hours, time to defer to you experts. I am constructing an XML representation of some purchase order data, here is the format I need:

<Orders xmlns="http://...">
    <Order>
        <Header>
            <OrderHeader>
                <TradingPartnerId></TradingPartnerId>
                <PurchaseOrderNumber></PurchaseOrderNumber>
                <TsetPurposeCode></TsetPurposeCode>
                <PrimaryPOTypeCode></PrimaryPOTypeCode>
                <PurchaseOrderDate></PurchaseOrderDate>
                <Vendor></Vendor>
            </OrderHeader>
            <PaymentTerms>
                <TermsDescription></TermsDescription>
            </PaymentTerms>
            <Dates>
                <DateTimeQualifier></DateTimeQualifier>
                <Date></Date>
            </Dates>
            <Address>
                <AddressTypeCode></AddressTypeCode>
            </Address>
        </Header>
        <LineItem>
            <OrderLine>
                <BuyerPartNumber></BuyerPartNumber>
                <VendorPartNumber></VendorPartNumber>
                <OrderQty></OrderQty>
                <OrderQtyUOM></OrderQtyUOM>
                <PurchasePrice></PurchasePrice>
            </OrderLine>
            <ProductOrItemDescription>
                <ProductCharacteristicCode></ProductCharacteristicCode>
                <ProductDescription></ProductDescription>
            </ProductOrItemDescription>
        </LineItem>
        <LineItem>
            <OrderLine>
                <BuyerPartNumber></BuyerPartNumber>
                <VendorPartNumber></VendorPartNumber>
                <OrderQty></OrderQty>
                <OrderQtyUOM></OrderQtyUOM>
                <PurchasePrice></PurchasePrice>
            </OrderLine>
            <ProductOrItemDescription>
                <ProductCharacteristicCode></ProductCharacteristicCode>
                <ProductDescription></ProductDescription>
            </ProductOrItemDescription>
        </LineItem>
        <Summary>
            <TotalAmount></TotalAmount>
            <TotalLineItemNumber></TotalLineItemNumber>
        </Summary>
    </Order>
</Orders>

Here is my query so far (try to hold back the screams...):

SELECT 
    (
        SELECT 
            (
                SELECT
                    LTRIM(RTRIM(VENDORNO)) AS "TradingPartnerId",
                    LTRIM(RTRIM(PONUMBER)) AS "PurchaseOrderNumber",
                    '00' AS "TsetPurposeCode",
                    'SA' AS "PrimaryPOTypeCode",
                    LTRIM(RTRIM(REPLACE(CONVERT(date, DATEX, 102), '.', '-'))) AS "PurchaseOrderDate",
                    LTRIM(RTRIM(VENDORNO)) AS "Vendor" 
                FROM [ECOMLIVE].[dbo].[POHEADER] 
                WHERE PONUMBER = 100203130
                FOR XML PATH ('OrderHeader'), TYPE
            ), 
            (
                SELECT
                    '30' AS "TermsDescription"
                FROM [ECOMLIVE].[dbo].[POHEADER]
                WHERE PONUMBER = 100203130
                FOR XML PATH ('PaymentTerms'), TYPE
            ),
            (
                SELECT
                    LTRIM(RTRIM(REQDATETYPE)) AS "DateTimeQualifier",
                    LTRIM(RTRIM(REPLACE(CONVERT(date, REQDATE, 102), '.', '-'))) AS "Date"
                FROM [ECOMLIVE].[dbo].[POHEADER]
                WHERE PONUMBER = 100203130
                FOR XML PATH ('Dates'), TYPE
            ),
            (
                SELECT
                    'ST' AS "AddressTypeCode"
                FROM [ECOMLIVE].[dbo].[POHEADER]
                WHERE PONUMBER = 100203130
                FOR XML PATH ('Address'), TYPE
            )
        FROM [ECOMLIVE].[dbo].[POHEADER]
        WHERE PONUMBER = 100203130
        FOR XML PATH ('Header'), TYPE
    ),
    (
        SELECT
            (
                SELECT
                    EDPNO AS "BuyerPartNumber",
                    VENDORNO AS "VendorPartNumber",
                    POQTY AS "OrderQty",
                    'EA' AS "OrderQtyUOM",
                    ACTUALCOST AS "PurchasePrice"
                FROM [ECOMLIVE].[dbo].[PODETAILS]
                WHERE PONUMBER = 100203130
                FOR XML PATH ('OrderLine'), TYPE
            )
        FROM [ECOMLIVE].[dbo].[PODETAILS]
        WHERE PONUMBER = 100203130
        FOR XML PATH ('LineItem'), TYPE
    )
FROM [ECOMLIVE].[dbo].[POHEADER]
WHERE PONUMBER = 100203130
FOR XML PATH ('Order'), ROOT ('Orders'), TYPE

I was doing okay until I got to the LineItem portion. When the query goes to grab the 8 line items, it's creating the appropriate 8 elements, but incorrectly places 8 elements inside. Like this:

Incorrect format:

<LineItem>
    <OrderLine>
        <a></a>
    </OrderLine>
    <OrderLine>
        <b></b>
    </OrderLine>
    <OrderLine>
        <c></c>
    </OrderLine>
</LineItem>
<LineItem>
    <OrderLine>
        <a></a>
    </OrderLine>
    <OrderLine>
        <b></b>
    </OrderLine>
    <OrderLine>
        <c></c>
    </OrderLine>
</LineItem>
...

Correct format:

<LineItem>
    <OrderLine>
        <a></a>
    </OrderLine>
</LineItem>
<LineItem>
    <OrderLine>
        <b></b>
    </OrderLine>
</LineItem>
<LineItem>
    <OrderLine>
        <c></c>
    </OrderLine>
</LineItem>
...

Thank you for your time!


Solution

  • Okay, this might not be the best solution, but here's how I solved it:

    This:

    ...
    (
        SELECT
            (
                SELECT
                    EDPNO AS "BuyerPartNumber",
                    VENDORNO AS "VendorPartNumber",
                    POQTY AS "OrderQty",
                    'EA' AS "OrderQtyUOM",
                    ACTUALCOST AS "PurchasePrice"
                FROM [ECOMLIVE].[dbo].[PODETAILS]
                WHERE PONUMBER = 100203130
                FOR XML PATH ('OrderLine'), TYPE
            )
        FROM [ECOMLIVE].[dbo].[PODETAILS]
        WHERE PONUMBER = 100203130
        FOR XML PATH ('LineItem'), TYPE
    )
    ...
    

    Was replaced with this:

    ...
    (
        SELECT
            EDPNO AS "OrderLine/BuyerPartNumber",
            VENDORNO AS "OrderLine/VendorPartNumber",
            POQTY AS "OrderLine/OrderQty",
            'EA' AS "OrderLine/OrderQtyUOM",
            ACTUALCOST AS "OrderLine/PurchasePrice"
        FROM [ECOMLIVE].[dbo].[PODETAILS]
        WHERE PONUMBER = 100203130
        FOR XML PATH ('LineItem'), TYPE
    )
    ...
    

    Adding "OrderLine/" as a prefix to each of the aliases moved them into the desired hierarchy! I apologize for answering my own question, feel free to add additional answers for improvements or alternatives.