[英]SQL Server, query multiple XML records in a table for a value, when multiple nodes/elements have the same name
我有一个名为xml_table的表,其中包含两列:unique_id,xml_data。 表中的每条记录在xml_data列中都有不同的XML数据。 我们假设XML模式如下所示。
<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>
现在让我们说我正在搜索产品B.我希望从表中所有PROD / @ NAME ='B'的所有记录中得到如下所示的结果集...
| ORDER_NAME | ORDER_LINE | PROD_NAME | UOM_WEIGHT |
如果我只从表中搜索XML的单个记录,我可以做这样的事情......
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'
通过将数字放在括号[1],[2]等中,因为我确切地知道哪些元素索引具有我正在寻找的产品。
但是,我需要的是xml_table中所有记录的相同结果集,而我正在寻找的产品可能在任何order_line元素中。 此外,任何xml_Data中的元素总数是未知的。
有没有办法有效地使元素级别成为外卡? 我知道这不是正确的语法,但有点像......
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'
...而不是元素树的硬编码整数,它会查看所有元素,以便它返回具有我正在寻找的特定值的任何元素的结果? 这实际上是我想要做的。
如果我理解你的问题
例
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'
返回
ORDER_NAME ORDER_LINE PROD_NAME UOM_WEIGHT
4800924 2 B 0.32
没有WHERE
ORDER_NAME ORDER_LINE PROD_NAME UOM_WEIGHT
4800924 1 A 0.31
4800924 2 B 0.32
4800924 3 C 0.33
以下代码将显示一些技术,以获得您想要的:
DECLARE @xml_table TABLE(ID INT IDENTITY, Comment VARCHAR(100),xml_data XML);
INSERT INTO @xml_table VALUES
('Your example'
,N'<ORDER ORDER_NAME="4800924">
<ORDER_LINE LINE_NUM="1">
<PROD NAME="A">
<UOM_WEIGHT>0.31</UOM_WEIGHT>
</PROD>
</ORDER_LINE>
<ORDER_LINE LINE_NUM="2">
<PROD NAME="B">
<UOM_WEIGHT>0.32</UOM_WEIGHT>
</PROD>
</ORDER_LINE>
<ORDER_LINE LINE_NUM="3">
<PROD NAME="C">
<UOM_WEIGHT>0.33</UOM_WEIGHT>
</PROD>
</ORDER_LINE>
</ORDER>')
,('Example without a "B"'
,N'<ORDER ORDER_NAME="4800924">
<ORDER_LINE LINE_NUM="1">
<PROD NAME="A">
<UOM_WEIGHT>0.31</UOM_WEIGHT>
</PROD>
</ORDER_LINE>
<ORDER_LINE LINE_NUM="2">
<PROD NAME="Other">
<UOM_WEIGHT>0.32</UOM_WEIGHT>
</PROD>
</ORDER_LINE>
<ORDER_LINE LINE_NUM="3">
<PROD NAME="C">
<UOM_WEIGHT>0.33</UOM_WEIGHT>
</PROD>
</ORDER_LINE>
</ORDER>')
,('Example with two "Bs"'
,N'<ORDER ORDER_NAME="4800924">
<ORDER_LINE LINE_NUM="1">
<PROD NAME="A">
<UOM_WEIGHT>0.31</UOM_WEIGHT>
</PROD>
</ORDER_LINE>
<ORDER_LINE LINE_NUM="2">
<PROD NAME="B">
<UOM_WEIGHT>0.32</UOM_WEIGHT>
</PROD>
</ORDER_LINE>
<ORDER_LINE LINE_NUM="3">
<PROD NAME="B">
<UOM_WEIGHT>0.33</UOM_WEIGHT>
</PROD>
</ORDER_LINE>
</ORDER>');
- 查询将使用.exist()
将行过滤为至少有一个NAME="B"
。 如果您的表有很多行,这将加快您的查询速度。 另一种方法是读取所有内容,并在最终结果集中使用WHERE
。 这可能会导致巨大的开销。
- 查询将使用.nodes()
来获取所有<ORDER_LINE>
节点的派生表。 每个节点单独返回。 在其中一个上使用[1]
是完全可以的
- 查询在XQuery predicate
使用sql:variable()
来过滤外部声明的变量。 这允许您使用具有各种过滤器值的相同搜索。
DECLARE @SearchFor VARCHAR(10)='B';
select ID
,Comment
,xml_data.value('(/ORDER/@ORDER_NAME)[1]', 'varchar(max)') as ORDER_NAME
,ol.value('@LINE_NUM', 'int') as ORDER_LINE
,ol.value('(PROD/@NAME)[1]', 'varchar(max)') as PROD_NAME
,ol.value('(PROD/UOM_WEIGHT)[1]', 'decimal(10,4)') as [WEIGHT]
from @xml_table AS xml_table
cross apply xml_data.nodes('/ORDER/ORDER_LINE') AS A(ol)
where xml_data.exist('/ORDER/ORDER_LINE/PROD[@NAME=sql:variable("@SearchFor")]') = 1;
结果(ID = 2未显示)
+----+--------------------------+------------+------------+-----------+--------+
| ID | Comment | ORDER_NAME | ORDER_LINE | PROD_NAME | WEIGHT |
+----+--------------------------+------------+------------+-----------+--------+
| 1 | Your example | 4800924 | 1 | A | 0.3100 |
+----+--------------------------+------------+------------+-----------+--------+
| 1 | Your example | 4800924 | 2 | B | 0.3200 |
+----+--------------------------+------------+------------+-----------+--------+
| 1 | Your example | 4800924 | 3 | C | 0.3300 |
+----+--------------------------+------------+------------+-----------+--------+
| 3 | Example with two "Bs" | 4800924 | 1 | A | 0.3100 |
+----+--------------------------+------------+------------+-----------+--------+
| 3 | Example with two "Bs" | 4800924 | 2 | B | 0.3200 |
+----+--------------------------+------------+------------+-----------+--------+
| 3 | Example with two "Bs" | 4800924 | 3 | B | 0.3300 |
+----+--------------------------+------------+------------+-----------+--------+
暗示
您可以使用cross apply
更改此行
cross apply xml_data.nodes('/ORDER/ORDER_LINE[PROD/@NAME=sql:variable("@SearchFor")]') AS A(ol)
...如果你想只获得“B”行。 在这种情况下,你可能没有WHERE ....exist()
结果就是这种情况
+----+--------------------------+------------+------------+-----------+--------+
| ID | Comment | ORDER_NAME | ORDER_LINE | PROD_NAME | WEIGHT |
+----+--------------------------+------------+------------+-----------+--------+
| 1 | Your example | 4800924 | 2 | B | 0.3200 |
+----+--------------------------+------------+------------+-----------+--------+
| 3 | Example with two "Bs" | 4800924 | 2 | B | 0.3200 |
+----+--------------------------+------------+------------+-----------+--------+
| 3 | Example with two "Bs" | 4800924 | 3 | B | 0.3300 |
+----+--------------------------+------------+------------+-----------+--------+
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.