簡體   English   中英

當多個節點/元素具有相同名稱時,SQL Server在表中查詢值的多個XML記錄

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM