Search code examples
sql-serverxmlxpathxquerysqlxml

MSSQL XML Performance Issue


Sample Data:

<Data>
  <row preis="2.4000" anzahl="72.0000" rabatt="0.0000" mwst="16.0000" bez="tägliche Standreinigung" adoku_nr="1694351" adr_nr="95" anmpos_nr="-1" doku_nr="1694351" gen="0" pos="12" preis_vk="2.4000" preis_lp="2.4000" druck="1" artikel_nr="601" artposarten_nr="1" />
  <row preis="330.0000" anzahl="1.0000" rabatt="0.0000" mwst="15.0000" bez="Abwasseranschluß" adoku_nr="500570" adr_nr="95" anmpos_nr="1" doku_nr="500570" gen="0" pos="6" preis_vk="330.0000" preis_lp="330.0000" druck="1" artikel_nr="304" artposarten_nr="1" />
</Data>

My target is to have columns for adoku_nr(id of the row), name of attribute, attribute value.

I have achieved this via

select
  T.C.value('(../@adoku_nr)[1]', 'int') as ID,
  T.C.value('local-name(.)', 'nvarchar(128)') as Name,
  T.C.value('(.)[1]', 'nvarchar(max)') as Value
from @Data.nodes('/Data/row/@*') as T(C)

Result:

ID      Name    Value
1694351 preis   2.4000
1694351 anzahl  72.0000
1694351 rabatt  0.0000
1694351 mwst    16.0000
1694351 bez tägliche Standreinigung
1694351 adoku_nr    1694351
1694351 adr_nr  95
1694351 anmpos_nr   -1
1694351 doku_nr 1694351
1694351 gen 0
1694351 pos 12
1694351 preis_vk    2.4000
1694351 preis_lp    2.4000
1694351 druck   1
1694351 artikel_nr  601
1694351 artposarten_nr  1
500570  preis   330.0000
500570  anzahl  1.0000
500570  rabatt  0.0000
500570  mwst    15.0000
500570  bez Abwasseranschluß
500570  adoku_nr    500570
500570  adr_nr  95
500570  anmpos_nr   1
500570  doku_nr 500570
500570  gen 0
500570  pos 6
500570  preis_vk    330.0000
500570  preis_lp    330.0000
500570  druck   1
500570  artikel_nr  304
500570  artposarten_nr  1

Now my problem is if i execute this query on a larger xml, 120 Rows in my case, its getting super slow (4 seconds), the issue appear to be the ID Column, without it im back to the lower millisecond bracket. Is there a way to optimize this query to achieve the same goal but with better performance?


Solution

  • You can include this need as XQuery into your XPath

    Look at the [@adoku_nr=sql:variable("@rowId")]

    declare @data XML=
    '<Data>
      <row preis="2.4000" anzahl="72.0000" rabatt="0.0000" mwst="16.0000" bez="tägliche Standreinigung" adoku_nr="1694351" adr_nr="95" anmpos_nr="-1" doku_nr="1694351" gen="0" pos="12" preis_vk="2.4000" preis_lp="2.4000" druck="1" artikel_nr="601" artposarten_nr="1" />
      <row preis="999" anzahl="999" rabatt="9999" mwst="16.0000" bez="tägliche Standreinigung" adoku_nr="999" adr_nr="95" anmpos_nr="-1" doku_nr="1694351" gen="0" pos="12" preis_vk="2.4000" preis_lp="2.4000" druck="1" artikel_nr="601" artposarten_nr="1" />
    </Data>';
    
    declare @rowId varchar(100)='1694351'; --try with "999" here to get the other row
    select
      T.C.value('(../@adoku_nr)[1]', 'int') as ID,
      T.C.value('local-name(.)', 'nvarchar(128)') as Name,
      T.C.value('(.)[1]', 'nvarchar(max)') as Value
    from @Data.nodes('/Data/row[@adoku_nr=sql:variable("@rowId")]/@*') as T(C)
    

    UPDATE from my magic glass bulb

    With a .nodes() on the rows and a CROSS APPLY r.nodes() for the attributes you are walking straightly downwards. No expensive backward navigation needed...

    I assume you are looking for this:

    select
      r.value('@adoku_nr', 'int') as ID,
      a.value('local-name(.)', 'nvarchar(128)') as Name,
      a.value('.', 'nvarchar(max)') as Value
    from @Data.nodes('/Data/row') as A(r)
    cross apply r.nodes('@*') AS B(a)