1

我有一个字典,我在 C# 中序列化并存储在 MSSQL Server 上的 XML 列中。序列化的 XML 如下所示:

<ArrayOfKeyValueOfstringanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
  <KeyValueOfstringanyType>
     <Key>code</Key><Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:string">WFR 052</Value>
  </KeyValueOfstringanyType>
  <KeyValueOfstringanyType>
    <Key>type</Key><Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:string">Newsletter</Value>
  </KeyValueOfstringanyType>
</ArrayOfKeyValueOfstringanyType>

最终,我想获得价值,其中关键是“代码”。我采取的第一步是获取第一个值,而不考虑键。

SELECT [xml_column].value('(/ArrayOfKeyValueOfstringanyType/KeyValueOfstringanyType/Value)[1]','varchar(255)') as val
FROM [my_table]

我得到空值。我知道它与命名空间有关,因为当我尝试删除命名空间的相同查询时,我得到一个值。我见过其他一些使用命名空间的场景,但我的 XML 格式有点不同,我正在努力寻找正确的语法。

这是我看过的另一个问题:

XML 字段 - 查询

4

2 回答 2

2

您遇到的问题是由于 XML 中的模式规范造成的。如果 XML 文档中的节点是模式的一部分,则必须在查询该节点时指定该模式。或者,您可以为架构规范使用通配符。但是指定没有模式的节点名称不起作用(正如您所经历的那样)。

所以让我们看一个例子:

SQL小提琴

MS SQL Server 2008 架构设置

CREATE TABLE dbo.Tbl(id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, dict XML);

INSERT INTO dbo.Tbl(dict)
VALUES('<ArrayOfKeyValueOfstringanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
      <KeyValueOfstringanyType>
         <Key>code</Key><Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:string">WFR 052</Value>
      </KeyValueOfstringanyType>
      <KeyValueOfstringanyType>
        <Key>type</Key><Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" i:type="d3p1:string">Newsletter</Value>
      </KeyValueOfstringanyType>
    </ArrayOfKeyValueOfstringanyType>');

该表dbo.Tbl仅使用两列创建,一个标识id列和一个dict用于 XML 的列。

要使您的第一个查询正常工作,请为每个节点使用通配符指定架构:

查询 1

SELECT dict.value('/*:ArrayOfKeyValueOfstringanyType[1]/*:KeyValueOfstringanyType[1]/*:Key[1]','NVARCHAR(MAX)')
FROM dbo.Tbl;

这导致第一个Key被返回:

结果

| COLUMN_0 |
|----------|
|     code |

现在,您要返回 Key = 'code' 的所有键值对的值节点。您可以在 xquery 中进行过滤,但我通常更喜欢在 SQL 中进行过滤。为此,我们首先需要取回所有对。XML 节点函数让我们更进一步:

查询 2

SELECT id,key_value.query('.')
  FROM dbo.Tbl
 CROSS APPLY dict.nodes('/*:ArrayOfKeyValueOfstringanyType/*:KeyValueOfstringanyType') AS N(key_value);

KeyValueOfstringanyType每个节点返回一行:

结果

| ID |                                                                                                                                                                                                                                                                                                      COLUMN_1 |
|----|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|  1 |    <p1:KeyValueOfstringanyType xmlns:p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays"><p1:Key>code</p1:Key><p1:Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="d3p1:string">WFR 052</p1:Value></p1:KeyValueOfstringanyType> |
|  1 | <p1:KeyValueOfstringanyType xmlns:p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays"><p1:Key>type</p1:Key><p1:Value xmlns:d3p1="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="d3p1:string">Newsletter</p1:Value></p1:KeyValueOfstringanyType> |

Key使用它,我们可以Value使用 XML.value 函数: 查询 3

SELECT id,
       key_value.value('./*:Key[1]','NVARCHAR(MAX)') AS [key],
       key_value.value('./*:Value[1]','NVARCHAR(MAX)') AS [value]
  FROM dbo.Tbl
 CROSS APPLY dict.nodes('/*:ArrayOfKeyValueOfstringanyType/*:KeyValueOfstringanyType') AS N(key_value);

现在我们每个键值对都有一行,键和值在不同的列中:

结果

| ID |  KEY |      VALUE |
|----|------|------------|
|  1 | code |    WFR 052 |
|  1 | type | Newsletter |

从那里很容易在WHERE子句中应用其他过滤器:

问题 4

WITH KeyValues AS(
SELECT id,
       key_value.value('./*:Key[1]','NVARCHAR(MAX)') AS [key],
       key_value.value('./*:Value[1]','NVARCHAR(MAX)') AS [value]
  FROM dbo.Tbl
 CROSS APPLY dict.nodes('/*:ArrayOfKeyValueOfstringanyType/*:KeyValueOfstringanyType') AS N(key_value)
)
SELECT *
  FROM KeyValues
 WHERE [Key] = 'code';

结果

| ID |  KEY |   VALUE |
|----|------|---------|
|  1 | code | WFR 052 |
于 2013-10-31T18:53:06.490 回答
0

Sebastian Meine 的回答很好,尽管在进行这样的查询时,我更喜欢在 nodes() 函数中通过 XPATH 过滤数据,如下所示:

select
    a.c.value('*:Value[1]','nvarchar(max)') as [value]
from Table1 as t
    outer apply t.Data.nodes('
        *:ArrayOfKeyValueOfstringanyType/*:KeyValueOfstringanyType[*:Key="code"]'
    ) as a(c)

它通常比解析整个 xml 然后通过 where 子句过滤所需的键更快。

sql fiddle example

于 2013-11-01T10:52:20.913 回答