简体   繁体   English

当多个节点/元素具有相同名称时,SQL Server在表中查询值的多个XML记录

[英]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. 我有一个名为xml_table的表,其中包含两列:unique_id,xml_data。 Each record in the table has different XML data in the xml_data column. 表中的每条记录在xml_data列中都有不同的XML数据。 Let's assume the XML schema looks something like below. 我们假设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>

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'... 现在让我们说我正在搜索产品B.我希望从表中所有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... 如果我只从表中搜索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'

by putting numbers in the brackets [1], [2] etc because I know exactly which element indexes have the product I am looking for. 通过将数字放在括号[1],[2]等中,因为我确切地知道哪些元素索引具有我正在寻找的产品。

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. 但是,我需要的是xml_table中所有记录的相同结果集,而我正在寻找的产品可能在任何order_line元素中。 Also, the total number of elements in any xml_Data is unknown. 此外,任何xml_Data中的元素总数是未知的。

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. 这实际上是我想要做的。

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 没有WHERE

ORDER_NAME  ORDER_LINE  PROD_NAME   UOM_WEIGHT
4800924     1           A           0.31
4800924     2           B           0.32
4800924     3           C           0.33

The following code will show some techniques to get what you want: 以下代码将显示一些技术,以获得您想要的:

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>');

--The query will use .exist() to filter the rows to those with at least one NAME="B" . - 查询将使用.exist()将行过滤为至少有一个NAME="B" If your table has got a lot of rows this will speed up your query. 如果您的表有很多行,这将加快您的查询速度。 The alternative is to read the whole bunch of everything and use a WHERE at the final result set. 另一种方法是读取所有内容,并在最终结果集中使用WHERE This can lead to a huge overhead. 这可能会导致巨大的开销。

--The query will use .nodes() to get a derived table of all your <ORDER_LINE> nodes. - 查询将使用.nodes()来获取所有<ORDER_LINE>节点的派生表。 Each node is returned alone. 每个节点单独返回。 Using [1] on one of these is perfectly okay then 在其中一个上使用[1]是完全可以的

--The query uses sql:variable() within an XQuery predicate to filter against an externally declared variable. - 查询在XQuery predicate使用sql:variable()来过滤外部声明的变量。 This allows you to use the same search with various filter values. 这允许您使用具有各种过滤器值的相同搜索。

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;

The result (ID=2 does not show up) 结果(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 |
+----+--------------------------+------------+------------+-----------+--------+

hint 暗示
You can change the line with cross apply to this 您可以使用cross apply更改此行

cross apply xml_data.nodes('/ORDER/ORDER_LINE[PROD/@NAME=sql:variable("@SearchFor")]') AS A(ol)

...if you want to get only the "B" rows back. ...如果你想只获得“B”行。 In this case you might go without the WHERE ....exist() 在这种情况下,你可能没有WHERE ....exist()

The result in this case 结果就是这种情况

+----+--------------------------+------------+------------+-----------+--------+
| 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.

相关问题 SQL Server -XML节点不返回多个记录 - SQL Server -XML nodes not returning multiple records XML to SQL - 选择多个同名节点 - XML to SQL - Selecting multiple nodes with the same name SQL FOR XML 生成多个同名节点 - SQL FOR XML to generate multiple same name nodes SQL - XML - 在子查询中创建多个同名节点 - SQL - XML - Create multiple nodes with same name in subquery SQL Server-用于XML路径(嵌套元素,多个记录) - SQL Server - For XML Path (nested elements, multiple records) SQL Server 2005查询以获取多个在QC项目数据库中具有相同模式的记录 - SQL Server 2005 Query for getting multiple records which have same pattern in QC project database SQL查询,用于从多个表中查找具有表名的所有记录 - SQL query for finding all records with Table Name from multiple table SQL 服务器解析 XML 到表 - 同名的多个节点和第一个节点应该是表列 - SQL Server parse XML to table - multiple node with the same name and first node should be table columns 当多个节点共享相同的名称时,使用 Oracle SQL 获取 JSON_VALUE - Get JSON_VALUE with Oracle SQL when multiple nodes share the same name SQL查询可获取同一服务器上多个数据库中可用的特定表的总记录 - Sql query to get total records for specific table available in multiple databases on same server
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM