在XML列中查询序列化字典

时间:2013-10-31 17:53:53

标签: c# sql sql-server xml sqlxml

我有一个字典,我在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>

最终,我想获得Value,其中Key是“code”。我采取的第一步是获得第一个值,无论密钥如何。

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

我得到了空值。我知道它与命名空间有关,因为当我尝试移除命名空间的相同查询时,我得到一个值。我已经看到了一些其他的命名空间方案,但我的XML格式有点不同,我很难找到合适的语法。

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

XML Field - Query

2 个答案:

答案 0 :(得分:2)

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

让我们看一个例子:

SQL Fiddle

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列和XML的dict列。

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

查询1

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

这会导致返回第一个Key

<强> Results

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

现在,您希望返回Key ='code'的所有键值对的Value节点。您可以在xquery中进行过滤,但我通常更喜欢在SQL中进行过滤。为此,我们首先要回到所有对。 XML节点功能让我们更近了一步:

查询2

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

每个KeyValueOfstringanyType节点返回一行:

<强> Results

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

使用它,我们可以使用XML.value函数到达KeyValue查询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);

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

<强> Results

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

<强> Results

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

答案 1 :(得分: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