根据兄弟值查询 XML 数据以获取信息

Query XML data for information based on a sibling value

我有以下 XML 数据,我对这些数据的控制为零。请注意,它基本上是 属性 组的集合。我需要 select 一个 属性 的值,而另一个 属性 的值是 'true'。问题是,没有什么可以分组的,我不知道如何正确地关联事物。

这是 XML 数据,以及到目前为止我提出的查询:

DECLARE @xml xml = '
<Container>
  <Collection>
    <ItemName>SomeItem</ItemName>
    <IsDeletable>true</IsDeletable>
    <IsPersisted>false</IsPersisted>
  </Collection>
  <Collection>
    <ItemName>AnotherItem</ItemName>
    <IsDeletable>true</IsDeletable>
    <IsPersisted>true</IsPersisted>
    <ExistsInDB>true</ExistsInDB>
  </Collection>
  <Collection>
    <ItemName>ItemFoo</ItemName>
    <IsDeletable>true</IsDeletable>
    <IsPersisted>true</IsPersisted>
    <ExistsInDB>true</ExistsInDB>
  </Collection>
  <Collection>
    <ItemName>BarBazItem</ItemName>
    <IsDeletable>true</IsDeletable>
    <IsPersisted>true</IsPersisted>
    <ExistsInDB>false</ExistsInDB>
  </Collection>
</Container>
'

;WITH XmlStuff AS (
    SELECT CAST(xmlShredded.colXmlItem.query('local-name(.)') AS nvarchar(4000)) as XmlNodeName,
        xmlShredded.colXmlItem.value('.', 'nvarchar(4000)') AS XmlNodeValue
    FROM @xml.nodes('/*/Collection/child::node()') as xmlShredded(colXmlItem) 
)
SELECT *
FROM XmlStuff 

现在,我需要做的是为 "ExistsInDB" 为 'true' 的每个分组获取 "ItemName" 值。请注意 "ExistsInDB" 属性 在第一个 属性 集合中不存在(即它应该被视为 NULL/false)。

所以在这种情况下,我需要查询此 xml 数据,并取回以下集合:

另一个项目 ItemFoo

我不应该得到 "SomeItem" 或 "BarBazItem"。

我一直在拼命想弄清楚如何为 "Get all ItemName values where the associated ExistsInDB value is both present, and true".

制定查询

这可能吗?

嗨,也许尝试兄弟姐妹匹配

/Container/Collection/ItemName[../ExistsInDB='true']

获取其父项包含等于 "true".

的 ExistsInDb 子项的 ItemName 元素
DECLARE @xml xml = '
<Container>
  <Collection>
    <ItemName>SomeItem</ItemName>
    <IsDeletable>true</IsDeletable>
    <IsPersisted>false</IsPersisted>
  </Collection>
  <Collection>
    <ItemName>AnotherItem</ItemName>
    <IsDeletable>true</IsDeletable>
    <IsPersisted>true</IsPersisted>
    <ExistsInDB>true</ExistsInDB>
  </Collection>
  <Collection>
    <ItemName>ItemFoo</ItemName>
    <IsDeletable>true</IsDeletable>
    <IsPersisted>true</IsPersisted>
    <ExistsInDB>true</ExistsInDB>
  </Collection>
  <Collection>
    <ItemName>BarBazItem</ItemName>
    <IsDeletable>true</IsDeletable>
    <IsPersisted>true</IsPersisted>
    <ExistsInDB>false</ExistsInDB>
  </Collection>
</Container>
';

SELECT node.value('.', 'nvarchar(100)')
FROM @xml.nodes('/Container/Collection/ItemName[../ExistsInDB="true"]') AS x(node) 

您可以使用:

;WITH XmlStuff AS (
    SELECT CAST(xmlShredded.colXmlItem.query('local-name(.)') AS nvarchar(4000)) as XmlNodeName,
        xmlShredded.colXmlItem.value('.', 'nvarchar(4000)') AS XmlNodeValue,
        xmlShredded.colXmlItem.value('(../ExistsInDB)[1]', 'nvarchar(4000)') AS ExistsInDB
    FROM @xml.nodes('/*/Collection/child::node()') as xmlShredded(colXmlItem) 
)
SELECT *
FROM XmlStuff 
WHERE ExistsInDB = 'true';

db<>fiddle demo

你的预期输出有点烦人,因为你两次提到BarBazItem

So in this case, I need to query this xml data, and get back the following set:

AnotherItem ItemFoo BarBazItem

I should NOT get "SomeItem" or "BarBazItem".

现有的两个答案都使用 backward-navigationparent-axis 通过 ../)。这是非常糟糕的表现(查找详细信息 here)并且不是最适合您的方法。试试这个:

SELECT nd.value('(ItemName/text())[1]','nvarchar(100)') AS ItemName
FROM @xml.nodes('/Container/Collection[ExistsInDB/text()="true"]') A(nd);

想法是,将谓词放在 <Collection> 之后的一级。您可以将其解读为

  • 深入<Container>
  • 深入 <Collection>,但我们只需要节点,其中有一个 text() 为“true”的子节点 <ExistsInDB>
  • A 编辑的列表 return 将包含每个 <Collection> 填充谓词(=过滤器)
  • 的一行
  • .value()-方法现在将在 <Collection> <ItemName> 和 return text()-节点下方的第一级。

我的查询 returns AnotherItemItemFoo

SomeItem 没有 returned 因为没有 <ExistsInDB>BarBazItem 没有 returned 因为 <ExistsInDB>.

中有“false”

更新

Lukasz Szoda 添加了 fiddle 并进行了一些改进。我认为最容易阅读 所有内容 的是:

SELECT nd.value('(ItemName/text())[1]','nvarchar(100)') AS ItemName
      ,nd.value('(IsDeletable/text())[1]','bit') AS IsDeletable
      ,nd.value('(IsPersisted/text())[1]','bit') AS IsPersisted
FROM @xml.nodes('/Container/Collection[ExistsInDB/text()="true"]') A(nd);