【问题标题】:How to query on XML column in SQL Server?如何在 SQL Server 中查询 XML 列?
【发布时间】:2021-05-16 16:29:57
【问题描述】:

我有一个名为“Table1”的表,表中有名为“Name”和“XMLDefinition”的列。

Name         XMLDefinition
--------------------------
Name1        xmlLink1
Name2        xmlLink2

在每个 XML 中,示例如下所示:

<Query xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <QueryView>
    <QueryKey />
  </QueryView>
  <Description>
  </Description>
  <QueryFields>
    <f />
    <f />
  </QueryFields>
  <FilterFields>
    <f ObjectName="TABLE5" ColumnName="ID">
      <DateFilterTypes />
      <FuzzyDateFilterTypes />
      <MonthDayFilterTypes />
      <Values>
        <v>0</v>
      </Values>
      <TranslatedValues>
        <v>No</v>
      </TranslatedValues>
      <DataType>Boolean</DataType>
    </f>   
    <f ObjectName="TABLE2" ColumnName="USERID">
      <DateFilterTypes />
      <FuzzyDateFilterTypes />
      <MonthDayFilterTypes />
      <Values>
        <v>B80055</v>
      </Values>
      <TranslatedValues>
        <v>B80055</v>
      </TranslatedValues>
      <DataType>String</DataType>
    </f>
  </FilterFields>
</Query>

只要在 XML 内容中包含 ObjectName = "TABLE2" AND ColumnName = "USERID",我想从 TABLE1 返回 Name。

我已经尝试了以下,虽然它没有出错,但它返回 0 条记录:

SELECT
a.Name,
X.Y.value('(f)[1]', 'VARCHAR(MAX)') as Object
FROM TABLE1 a
OUTER APPLY a.XMLDefinition.nodes('Query/FilterFields/f') as X(Y)
WHERE X.Y.value('(ObjectName)[1]', 'VARCHAR(MAX)') = 'TABLE2'
AND X.Y.value('(ColumnName)[1]', 'VARCHAR(MAX)') = 'USERID'

我不确定我错过了什么,因为我似乎正在从 Query > FilterFields > f 向下钻取,我假设我可以根据此处的 ObjectName 和 ColumnName 进行过滤。

尝试 2 更新:

SELECT Name from TABLE1
WHERE XMLDefinition.value('(/Query/QueryView/Description/QueryFields/FilterFields/f/@ObjectName) [1] ',' varchar(max)') = 'TABLE2'
AND XMLDefinition.value('(/Query/QueryView/Description/QueryFields/FilterFields/f/@ColumnName) [1] ',' varchar(max)') = 'USERID'

通过钻取每个标签尝试此尝试后,它仍然给我 0 个结果。

尝试 3 更新:

select
  a.Name,
  X.Y.query(N'.') as [Object] --this returns the XML of the <f> element
from dbo.Table1 a
cross apply a.XMLDefinition.nodes('//*:f[@ObjectName="TABLE2"][@ColumnName="USERID"][1]') as X(Y);

我不知道为什么,但我尝试了这个,现在它工作并返回了我正在寻找的结果。我是 XML 新手,但我认为这行得通,因为它忽略了 f 标记之前的所有命名空间和先前的标记?

【问题讨论】:

  • 是的,完全有可能。它是使用带有nodes() Method (xml Data Type)query() Method (xml Data Type) 方法的XPath 表达式来实现的。
  • 请做一些研究,尝试一下,遇到困难再发帖。
  • 您编辑到“尝试 2 更新”的 XPath 对您不起作用,因为 QueryViewDescriptionQueryFields 不在到达 @ 所需的路径中987654331@ - 他们是FilterFields 的兄弟姐妹,而不是它的父母。
  • 我发布了一个尝试 3,这次成功了。使用您的原始答案,我将最后一行更改为 CROSS APPLY a.XMLDefinition.nodes('//*:f[@ObjectName="TABLE2"][@ColumnName="USERID"][1]') 为 X(是)。仍然有点困惑为什么这有效,而不是以前的方式。
  • 写。尝试 3 更新...是的,命名空间很重要。如果//f... 不起作用但//*:f... 起作用(使用通配符命名空间),那是因为f 元素不像已发布的XML 示例那样位于匿名命名空间中。通配符命名空间可以工作,但效率不是特别高,如果在多个命名空间中声明相同的元素名称(这是允许的),有时会得到意想不到的结果。

标签: sql-server xml


【解决方案1】:

下面的代码可能符合您的要求。请注意,cross apply 将仅返回与 XPath 查询匹配的 dbo.Table 行,而 outer apply 将返回所有 dbo.Table 行,但仅返回与 XPath 查询匹配的行的 XML 派生值:

create table dbo.Table1 (
  Name nvarchar(10),
  XMLDefinition xml
);

insert dbo.Table1 (Name, XMLDefinition) values
  (N'Name1', N'<xmlLink1 />'),
  (N'Name2', N'<Query xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
  <FilterFields>
    <f ObjectName="TABLE2" ColumnName="USERID" ParentPath="TABLE2" DisplayPath="TABLE2" CompareType="Or" UseLeftParenthesis="true" LeftParenthesisCount="1" IncludeCurrentNode="true">
      <DateFilterTypes />
      <FuzzyDateFilterTypes />
      <MonthDayFilterTypes />
      <Values>
        <v>B80055</v>
      </Values>
      <TranslatedValues>
        <v>B80055</v>
      </TranslatedValues>
      <DataType>String</DataType>
    </f>
  </FilterFields>
</Query>');

select
  a.Name,
  X.Y.query(N'.') as [Object] --this returns the XML of the <f> element
from dbo.Table1 a
cross apply a.XMLDefinition.nodes(N'/Query/FilterFields/f[@ObjectName="TABLE2"][@ColumnName="USERID"][1]') as X(Y);

【讨论】:

  • 我尝试了您的示例代码,它确实有效,但是当我根据我的实际数据运行它时,由于某种原因它仍然给出了 0 条记录。我更新了上面的 XML 内容以显示它的整个层次结构,因为我最初遗漏了几个部分。所以基于上面更新的这个层次结构,它看起来像 CROSS APPLY a.XMLDefinition.nodes(N'/Query/FilterFields/f[@ObjectName="TABLE2"][@ColumnName="USERID"][1]') as X (Y);应该仍然有效,但仍然给出 0 条记录。现在应该是 Query > QueryView > Description > QueryFields > FilterFields > f 吗?
  • 您在问题中编辑的数据不是有效的 XML,因为它存在一些问题: 1. Query 标签有一个额外的 &gt; 符号; 2. f 标签缺少&gt; 符号。这是由于编辑错误,还是您将数据存储在 varchar/nvarchar 列中,因为它无法保存到 xml 列中(因为它无效)?
  • 我为这些错误道歉,我已经在上面修复了它们。在查看了实际的 XML 之后,您对那些 > 符号是正确的,这是我的编辑错误。看起来像 CROSS APPLY a.XMLDefinition.nodes(N'/Query/FilterFields/f[@ObjectName="TABLE2"][@ColumnName="USERID"][1]') 作为 X(Y);应该仍然有效,对吗?还是我需要改变我向下钻取到 f 标签的方式?
  • nodes() 中的 XPath 查询已经将您带到 f 标记。结果中的 Object 列已返回 f 标记的整个 XML。您需要返回的 f 标记是否有特定的内容,例如第一个 Values/v 标记的内容?
  • 我的目标主要是从 TABLE1 返回 Name 列,只要 XMLDefinition 列内容包含 TABLE2 作为 XML 中的 ObjectName 并且 ColumnName 等于 f 标签内 XML 中的 USERID
猜你喜欢
  • 1970-01-01
  • 2013-08-18
  • 2012-10-18
  • 2010-09-08
  • 2012-05-07
  • 2016-07-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多