【问题标题】:Querying Serialized Dictionary in XML Column在 XML 列中查询序列化字典
【发布时间】:2013-10-31 17:53:52
【问题描述】:

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

【问题讨论】:

    标签: c# sql sql-server xml sqlxml


    【解决方案1】:

    您遇到的问题是由于 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' 的所有键值对的值节点。您可以在 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 很好的详细答案,我仍然会在 nodes() 函数中按键添加过滤器
    【解决方案2】:

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-19
      • 2013-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-19
      • 2010-11-04
      相关资源
      最近更新 更多