Search code examples
xmlxqueryxquery-sqlopenrowset

XML data to SQL tables, difficulty gettting child node data


I have an xml file that I am loading into SQL. I have no problem getting the xml file loaded and the top level nodes into a sql table. I am having difficulty reaching the next level child node.

Here is an example xml file (similar to what I am trying to get into tables):

    <RSR:ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:RSR="urn:rsrNamespace" xsi:schemaLocation="urn:rsrNamespace SomeSchema.xsd">
      <ClientReport>
        <ClientID>05929</ClientID>
        <ClientFirstName>Eric</ClientFirstName>
        <ClientLastName>Jones</ClientLastName>
        <ClientService>
          <ClientServices>
            <ServiceID>250</ServiceID>
            <Salesperson>Charlie</Salesperson>
            <Cost>15.95</Cost>
          </ClientServices>
          <ClientServices>
            <ServiceID>356</ServiceID>
            <Salesperson>Jane</Salesperson>
            <Cost>34.25</Cost>
          </ClientServices>
        </ClientService>
      </ClientReport>
    </RSR:ROOT>

I have no problem getting the top level nodes, ClientID and ClientName, into a table. I can run this sql and get values into a temp table CLIENTREPORT:

CREATE TABLE ##XMLTable (XMLData XML)

SET @cmd = 'INSERT INTO ##XMLTable SELECT CONVERT(XML, BulkColumn) AS Bulkcolumn FROM OPENROWSET (BULK ''' + @FullPathName + ''', SINGLE_BLOB) as X'

SET @cmd = 'WITH XMLNAMESPACES (''urn:rsrNamespace'' AS p) 
INSERT INTO ##CLIENTREPORT (ClientID, ClientFullName)
SELECT
    CLIENTDATA.ClientReport.value(''(./ClientID)[1]'', ''VARCHAR(100)''),
    CLIENTDATA.ClientReport.value(''(./ClientFirstName)[1]'', ''VARCHAR(100)'') + '' '' + CLIENTDATA.ClientReport.value(''(./ClientLastName)[1]'', ''VARCHAR(100)'')
FROM ##XMLTable X
    CROSS APPLY X.XMLData.nodes(''p:ROOT/ClientReport'') AS CLIENTDATA (ClientReport)'

I am having an issue getting to those lower level nodes - ServiceID, Salesperson, Cost. Here is what I have that does not work:

SET @cmd = 'WITH XMLNAMESPACES (''urn:rsrNamespace'' AS p) 
INSERT INTO ##CLIENTREPORTSERVICES (ClientID, Salesperson)
SELECT
    CLIENTDATA.ClientReport.value(''(./ClientID)[1]'', ''VARCHAR(100)''),
    CLIENTSERVICEDATA.ClientServices.value(''(./Salesperson)[1]'', ''VARCHAR(25)'')
FROM ##XMLTable X
    CROSS APPLY X.XMLData.nodes(''p:ROOT/ClientReport'') AS CLIENTDATA (ClientReport)
    CROSS APPLY CLIENTDATA.ClientReport.nodes(''ClientService/ClientServices'') AS CLIENTSERVICEDATA (ClientServices)'

All I get are null values in the Salesperson field. What am I missing about XQuery and navigating to lower level nodes?


Solution

  • Please try the following.

    SQL

    -- DDL and sample data population, start
    DECLARE @tbl TABLE (XMLData XML);
    INSERT @tbl (XMLData) VALUES
    (N'<QSR:ROOT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:QSR="urn:qsrNamespace" xsi:schemaLocation="urn:qsrNamespace SomeSchema.xsd">
        <ClientReport>
            <ClientID>05929</ClientID>
            <ClientFirstName>Eric</ClientFirstName>
            <ClientLastName>Jones</ClientLastName>
            <ClientService>
                <ClientServices>
                    <ServiceID>250</ServiceID>
                    <Salesperson>Charlie</Salesperson>
                    <Cost>15.95</Cost>
                </ClientServices>
                <ClientServices>
                    <ServiceID>356</ServiceID>
                    <Salesperson>Jane</Salesperson>
                    <Cost>34.25</Cost>
                </ClientServices>
            </ClientService>
        </ClientReport>
    </QSR:ROOT>');
    -- DDL and sample data population, end
    
    ;WITH XMLNAMESPACES ('urn:qsrNamespace' AS QSR)
    SELECT c.value('(ClientID/text())[1]', 'VARCHAR(20)') AS ClientID
        , c.value('(ClientFirstName/text())[1]', 'VARCHAR(20)') AS ClientFirstName
        , c.value('(ClientLastName/text())[1]', 'VARCHAR(20)') AS ClientLastName
    FROM @tbl
    CROSS APPLY XMLData.nodes('/QSR:ROOT/ClientReport') AS t(c);
    
    ;WITH XMLNAMESPACES ('urn:qsrNamespace' AS QSR)
    SELECT p.value('(ClientID/text())[1]', 'VARCHAR(20)') AS ClientID
        , c.value('(ServiceID/text())[1]', 'VARCHAR(20)') AS ServiceID
        , c.value('(Salesperson/text())[1]', 'VARCHAR(20)') AS Salesperson
        , c.value('(Cost/text())[1]', 'DECIMAL(10,2)') AS Cost
    FROM @tbl
    CROSS APPLY XMLData.nodes('/QSR:ROOT/ClientReport') AS t1(p)
    CROSS APPLY p.nodes('ClientService/ClientServices') AS t2(c);
    

    Output #1

    ClientID ClientFirstName ClientLastName
    05929 Eric Jones

    Output #2

    ClientID ServiceID Salesperson Cost
    05929 250 Charlie 15.95
    05929 356 Jane 34.25