2

I have below typed xml

 <root>
    <row1 invoice_number="WEDRT" vendor="Telekm" amount="233" status="1" created_date="42590" />
    <row2 invoice_number="MSFDRT" vendor="ARS" amount="344" status="1" created_date="42955" />
</root>

This xml is generated dynamically. By dynamic I mean at the time of writing the sql code to parse it I don't know how many rows and attributes would be there. There could be as many rows as 100,000 and as many attributes as user wants. I won't even know the name of the attributes.

Is it possible to put it in a temp table by producing following type of result out of this xml?

Invoice_number     Vendor     amount   status    created_Date
WEDRT              TELEKOM     233       1        42590
MSFDRT             ARS         344       1        42955

I have tried the following

    DECLARE
    @xml    XML,
    @Columns VARCHAR(MAX)

SET @xml = '
<root>
<row1 invoice_number="WEDRT" vendor="Telekm" amount="233" status="1" created_date="42590" />
<row2 invoice_number="MSFDRT" vendor="ARS" amount="344" status="1" created_date="42955" />
</root>
'
BEGIN   
    SET NOCOUNT ON;

        DECLARE @COUNT INT, @COUNTER INT, @TQUERY   VARCHAR(2000), @SELECT  VARCHAR(MAX), @VALUES   VARCHAR(MAX)
        SELECT * INTO #TEMPTABLE
        FROM(
        SELECT  
            CAST(x.v.query('local-name(.)') AS VARCHAR(100)) As AttributeName,
            v.value('.' , 'VARCHAR(100)') AS Value
            FROM @XML.nodes('//@*') x(v)
            ) A

        SELECT * FROM #TEMPTABLE 

        DROP TABLE #TEMPTABLE
END

It gives me result like below table for 2 rows in xml

AttributeName     Value
invoice_number    WEDRT
vendor            Telekom
amount            233
status            1
created_Date      42590
invoice_number    MSFDRT
vendor            ARS
amount            344
status            1
created_Date      42955
5
  • 1
    What do you mean by "read"? What do you need from it in the end, in which form? Commented Sep 24, 2016 at 3:38
  • @RogerWolf: Sorry, I just updated the expected result. Commented Sep 24, 2016 at 4:31
  • You have a nice working solution, you should add row_number to your output, or some identity to ech invoice and then just PIVOT Commented Sep 24, 2016 at 6:18
  • @gofr1: Pivoting is fine but as you can see the attribute names are getting repeated after 5 rows. That's why i guess i can't pivot. Commented Sep 24, 2016 at 6:40
  • I add solution with dynamic pivoting, hope this helps. Commented Sep 24, 2016 at 7:11

2 Answers 2

4

I have modified your query, hope this helps.

DECLARE @xml    XML,
        @Columns VARCHAR(MAX)

SET @xml = '
<root>
<row1 invoice_number="WEDRT" vendor="Telekm" amount="233" status="1" created_date="42590" />
<row2 invoice_number="MSFDRT" vendor="ARS" amount="344" status="1" created_date="42955" />
</root>
'
BEGIN   
    SET NOCOUNT ON;

        DECLARE @COUNT INT, @COUNTER INT, @TQUERY   VARCHAR(2000), @SELECT  VARCHAR(MAX), @VALUES   VARCHAR(MAX)

        SELECT * INTO #TEMPTABLE
        FROM(
            SELECT  CAST(x.v.query('local-name(.)') AS VARCHAR(100)) As AttributeName,
                    x.v.value('.' , 'VARCHAR(100)') AS [Value],
                    CAST(x.v.query('local-name(..)') AS VARCHAR(100)) As RowNumber
            FROM @XML.nodes('//*//@*') x(v)
        ) A

        DECLARE @t TABLE (AttributeName nvarchar(max), r int)
        DECLARE @sql nvarchar(max),
                @col nvarchar(max)

        INSERT INTO @t
        SELECT DISTINCT AttributeName,
                ROW_NUMBER() OVER(PARTITION BY RowNumber ORDER BY RowNumber) as r
        FROM #TEMPTABLE 

        SELECT @col = (
            SELECT ','+QUOTENAME(AttributeName)
            FROM @t
            ORDER BY r
            FOR XML PATH('')
        )


        SELECT @sql = N'
        SELECT *
        FROM #TEMPTABLE t
        PIVOT (
            MAX([Value]) FOR AttributeName IN ('+STUFF(@col,1,1,'')+')
        ) as pvt'

        EXEC sp_executesql @sql

        DROP TABLE #TEMPTABLE
END

Output:

RowNumber   invoice_number  vendor  amount  status  created_date
row1        WEDRT           Telekm  233     1       42590
row2        MSFDRT          ARS     344     1       42955

The main idea is to dynamically pivot your results. To get rid of RowNumber column you can use SELECT '+STUFF(@col,1,1,'')+' instead of 'SELECT * part

Sign up to request clarification or add additional context in comments.

3 Comments

Beauty, this is just awesome. Thanks a lot. Is there a possible way to remove rownumber from the last selection?
Of course you can! I add this to my answer, see the very end of it.
I just did the same. Thanks again. I have got to use temporary tables instead of table variables because the data in the xml string gonna be huge.
-1

You are on the right track. Only change you need is to select all elements instead of all attributes:

declare @x xml = '<root>
  <row1 invoice_number="WEDRT" vendor="Telekm" amount="233" status="1" created_date="42590" />
  <row2 invoice_number="MSFDRT" vendor="ARS" amount="344" status="1" created_date="42955" />
</root>';

select t.c.value('local-name(.)', 'sysname') as [NodeName],
    t.c.value('./@invoice_number', 'varchar(50)') as [InvoiceNumber],
    t.c.value('./@vendor', 'varchar(50)') as [Vender],
    t.c.value('./@amount', 'money') as [Amount],
    t.c.value('./@status', 'int') as [Status],
    t.c.value('./@created_date', 'int') as [CreatedDate]
from @x.nodes('/root[1]/*') t(c);

I have added the node name as the first column, in case you might need it. Remove it from the query if you don't.

1 Comment

Thanks but at run time I don't know the name of the columns so I can't hard code as invoice-number, vendor etc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.