Запросите данные XML для получения информации на основе одноуровневого значения - PullRequest
0 голосов
/ 03 мая 2019

У меня есть следующие данные XML, которые я не контролирую.Обратите внимание, что это в основном коллекция групп свойств.Мне нужно выбрать значение одного свойства, где значение другого свойства равно «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 

Теперь мне нужно получить значение «ItemName» для каждой группы, где «ExistsInDB» равно «true».Обратите внимание, что свойство «ExistsInDB» не существует в первой коллекции свойств (т. Е. Его следует рассматривать как NULL / false).

Так что в этом случае мне нужно запросить эти данные XML и получить обратноследующий набор:

AnotherItem ItemFoo

Я не должен получать "SomeItem" или "BarBazItem".

Я бился головой о стол, пытаясь понять, каксформулировать запрос для «Получить все значения ItemName, если соответствующее значение ExistsInDB присутствует и истинно».

Возможно ли это вообще?

Ответы [ 3 ]

2 голосов
/ 04 мая 2019

Ваш ожидаемый результат немного раздражает, как вы упомянули BarBazItem дважды

Так что в этом случае мне нужно запросить эти данные XML и получить обратно следующий набор:

AnotherItem ItemFoo BarBazItem

Я НЕ должен получать «SomeItem» или «BarBazItem» .

В обоих существующих ответах используется обратная навигация (там же родительская ось через ../). Это очень плохое исполнение (подробности здесь ) и не лучший подход для вас. Попробуйте это:

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

Идея состоит в том, чтобы поместить предикат на один уровень выше <Collection>. Вы можете прочитать это как

  • Погружение в <Container>
  • Погрузитесь глубже в <Collection>, но нам нужны только узлы, где есть подузел <ExistsInDB> с text(), равным "true".
  • Список, возвращаемый A, будет включать одну строку на <Collection>, заполняющую предикат (= фильтр)
  • .value() -метод теперь возьмет на первом уровне ниже <Collection> <ItemName> и вернет text() -узел.

Мой запрос возвращает AnotherItem и ItemFoo

SomeItem не возвращается, так как нет <ExistsInDB> и BarBazItem не возвращается, поскольку в <ExistsInDB>.

есть "false".

UPDATE

Лукаш Сзода добавил скрипку с некоторыми улучшениями. Я думаю, что проще всего прочитать все это:

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);
2 голосов
/ 04 мая 2019

Привет, возможно, попробуй матч с братом или сестрой

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

Получает элементы ItemName, чьи родители содержат дочерний элемент ExistsInDb, равный «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) 
1 голос
/ 04 мая 2019

Вы можете использовать:

;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

...