Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL Server, query multiple XML records in a table for a value, when multiple nodes/elements have the same name

I have a table called xml_table with two columns: unique_id, xml_data. Each record in the table has different XML data in the xml_data column. Let's assume the XML schema looks something like below.

<ORDER ORDER_NAME="4800924">
  <ORDER_LINE LINE_NUM="1">
    <PROD NAME="A">
      <UOM_WEIGHT>0.31</UOM_WEIGHT>
    </PROD NAME>
  </ORDER_LINE>
  <ORDER_LINE LINE_NUM="2">
    <PROD NAME="B">
      <UOM_WEIGHT>0.32</UOM_WEIGHT>
    </PROD NAME>
  </ORDER_LINE>
  <ORDER_LINE LINE_NUM="3">
    <PROD NAME="C">
      <UOM_WEIGHT>0.33</UOM_WEIGHT>
    </PROD NAME>
  </ORDER_LINE>
</ORDER>

Now let's say I am searching for product B. I want the get a result set shown below from ALL the records in the table where any PROD/@NAME = 'B'...

   | ORDER_NAME | ORDER_LINE | PROD_NAME | UOM_WEIGHT |

If I were searching ONLY a single record of XML from the table I could do something like this...

select xml_data.value('(/ORDER/@ORDER_NAME)[1]', 'varchar(max)') as ORDER_NAME,
   xml_data.value('(/ORDER/ORDER_LINE/@LINE_NUM)[2]', 'varchar(max)') as ORDER_LINE,
   xml_data.value('(/ORDER/ORDER_LINE/PROD/@NAME)[2]', 'varchar(max)') as PROD_NAME,
   xml_data.value('(/ORDER/ORDER_LINE/PROD/UOM_WEIGHT)[2]', 'varchar(max)') as WEIGHT
from xml_table 
where  unique_id = 'blah'

by putting numbers in the brackets [1], [2] etc because I know exactly which element indexes have the product I am looking for.

However, what I need is this same result set for ALL records in the xml_table, while the product I'm looking for could be in ANY order_line element. Also, the total number of elements in any xml_Data is unknown.

Is there a way to effectively make the element level a wild card? I know this is not correct syntax, but something like...

select xml_data.value('(/ORDER/@ORDER_NAME)[1]', 'varchar(max)') as ORDER_NAME,
   xml_data.value('(/ORDER/ORDER_LINE/@LINE_NUM)[*]', 'varchar(max)') as ORDER_LINE,
   xml_data.value('(/ORDER/ORDER_LINE/PROD/@NAME)[*]', 'varchar(max)') as PROD_NAME,
   xml_data.value('(/ORDER/ORDER_LINE/PROD/UOM_WEIGHT)[*]', 'varchar(max)') as WEIGHT
from xml_table 
where  xml_data.value('(/ORDER/ORDER_LINE/PROD/@NAME)[*]', 'varchar(max)') = 'B'

... where instead of a hard coded integer for the element tree, it looks at all elements so that it returns results from ANY element that has the specific value I am looking for? That is effectively what I am trying to do.

like image 243
684165342 Avatar asked Jan 29 '26 23:01

684165342


1 Answers

If I understand your question

Example

Select ORDER_NAME   = lvl1.n.value('@ORDER_NAME','int') 
      ,ORDER_LINE   = lvl2.n.value('@LINE_NUM','int') 
      ,PROD_NAME    = lvl3.n.value('@NAME','varchar(50)') 
      ,UOM_WEIGHT   = lvl3.n.value('UOM_WEIGHT[1]','decimal(10,2)') 
 From  @x.nodes('*') lvl1(n)
 Cross Apply lvl1.n.nodes('ORDER_LINE') lvl2(n)
 Cross Apply lvl2.n.nodes('PROD') lvl3(n)
 Where lvl3.n.value('@NAME','varchar(50)') ='B'

Returns

ORDER_NAME  ORDER_LINE  PROD_NAME   UOM_WEIGHT
4800924     2           B           0.32

Without the WHERE

ORDER_NAME  ORDER_LINE  PROD_NAME   UOM_WEIGHT
4800924     1           A           0.31
4800924     2           B           0.32
4800924     3           C           0.33
like image 153
John Cappelletti Avatar answered Jan 31 '26 14:01

John Cappelletti



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!