Search code examples
sql-servert-sqlsqlxml

Query XML data for information based on a sibling value


I have the following XML data, which I have zero control over. Note that it's basically a collection of property groups. I need to select the value of one property where the value of another property is 'true'. The problem is, there's nothing to group on, and I cannot figure out how to associate things correctly.

Here's the XML Data, and the query I came up with so far:

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 

Now, what I need to do, is get the "ItemName" value for each grouping where "ExistsInDB" is 'true'. Note that the "ExistsInDB" property doesn't exist in the first property collection (i.e. it should be considered NULL/false).

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

AnotherItem ItemFoo

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

I've been beating my head against the desk trying to figure out how to formulate the query for "Get all ItemName values where the associated ExistsInDB value is both present, and true".

Is this even possible?


Solution

  • Hi perhaps try a sibling match

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

    That gets ItemName elements whose parents contain an ExistsInDb child equal to "true".

    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)