我有一个字典,我在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格式有点不同,我很难找到合适的语法。
这是我看到的另一个问题:
答案 0 :(得分:2)
您遇到的问题是由XML中的架构规范引起的。如果XML文档中的节点是模式的一部分,则必须在查询该节点时指定该模式。或者,您可以使用通配符作为架构规范。但是,指定没有架构的节点名称不起作用(正如您所经历的那样)。
让我们看一个例子:
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函数到达Key
和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);
现在我们每个键值对都有一行,键和值在不同的列中:
<强> 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 强>