XQuery T- ​​sql вставляет узел во все элементы - PullRequest
2 голосов
/ 21 января 2020

Я хочу вставить один узел во все элементы переменной ans XML без использования курсора. Я использую переменную xml здесь, но буду работать на столе. Мне нужно будет запускать это каждые несколько дней или около того, и я не хочу добавлять дубликаты элементов. Я тестировал, используя «если», но, похоже, он не работал без «другого», и я не мог найти, как «иначе ничего не делать». Кстати, я не могу изменить формат xml.

Вот xml:

Declare @xml xml =
'<?xml version="1.0" ?>
<DataObject>
    <Objects>
        <Object Name="FirstName" Value="John" />
        <Object Name="LastName" Value="Smith" />
        <Object Name="City" Value="Miami" />
        <Object Name="State" Value="FL" />
        <Object Name="Department" Value="HR" />
    </Objects>
</DataObject>
<DataObject>
    <Objects>
        <Object Name="FirstName" Value="Jane" />
        <Object Name="LastName" Value="Doe" />
        <Object Name="City" Value="Hollywood" />
        <Object Name="State" Value="FL" />
        <Object Name="Department" Value="Accounting" />
    </Objects>
</DataObject>'

В псевдокоде я хочу сказать

Insert <Object Name="Zip" Value="" />
Into all <DataObject/Objects>
After "all" (//DataObject/Objects/Object[@Name="State"])
Where not exixt (<Object Name="Zip" Value="" />)

Другими словами, я хочу добавить узел Zip ко всем элементам Object, у которых еще нет узла zip. Я хочу сделать это с помощью одного оператора вместо использования курсора.

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

Большое спасибо !!

1 Ответ

1 голос
/ 21 января 2020

Из тега [tsql] и данного примера я предполагаю, что это SQL -Server. Пожалуйста, не забудьте пометить свой следующий вопрос соответствующим образом (продукт и версия).

Очень ограниченная функция .modify() допускает одно единственное изменение на вызов.

Лучший подход во многом зависит по фактической проблеме ...

Вы можете пройти по маршруту FLWOR :

Сначала я объявляю таблицу макетов с тремя строками. Одна строка - ваш образец, одна строка уже содержит один Zip, а третья - ZIP в обоих XML фрагментах.

Declare @mockupTable TABLE(ID INT IDENTITY, SomeDescription VARCHAR(100), YourXml XML);
INSERT INTO @mockupTable VALUES
('Your sample, no Zip codes',
'<DataObject>
    <Objects>
        <Object Name="FirstName" Value="John" />
        <Object Name="LastName" Value="Smith" />
        <Object Name="City" Value="Miami" />
        <Object Name="State" Value="FL" />
        <Object Name="Department" Value="HR" />
    </Objects>
</DataObject>
<DataObject>
    <Objects>
        <Object Name="FirstName" Value="Jane" />
        <Object Name="LastName" Value="Doe" />
        <Object Name="City" Value="Hollywood" />
        <Object Name="State" Value="FL" />
        <Object Name="Department" Value="Accounting" />
    </Objects>
</DataObject>')
,('One has a zip code already',
'<DataObject>
    <Objects>
        <Object Name="FirstName" Value="John" />
        <Object Name="LastName" Value="Smith" />
        <Object Name="City" Value="Miami" />
        <Object Name="State" Value="FL" />
        <Object Name="Zip" Value="12345" />
        <Object Name="Department" Value="HR" />
    </Objects>
</DataObject>
<DataObject>
    <Objects>
        <Object Name="FirstName" Value="Jane" />
        <Object Name="LastName" Value="Doe" />
        <Object Name="City" Value="Hollywood" />
        <Object Name="State" Value="FL" />
        <Object Name="Department" Value="Accounting" />
    </Objects>
</DataObject>')
,('Both have zip codes',
'<DataObject>
    <Objects>
        <Object Name="FirstName" Value="John" />
        <Object Name="LastName" Value="Smith" />
        <Object Name="City" Value="Miami" />
        <Object Name="State" Value="FL" />
        <Object Name="Zip" Value="12345" />
        <Object Name="Department" Value="HR" />
    </Objects>
</DataObject>
<DataObject>
    <Objects>
        <Object Name="FirstName" Value="Jane" />
        <Object Name="LastName" Value="Doe" />
        <Object Name="City" Value="Hollywood" />
        <Object Name="State" Value="FL" />
        <Object Name="Zip" Value="12345" />
        <Object Name="Department" Value="Accounting" />
    </Objects>
</DataObject>');

- это запрос

SELECT t.*
      ,t.YourXml.query
      ('
       for $os in /DataObject/Objects
       return
        <DataObject>
        <Objects>
        {
            for $o in $os/Object
            return
            if(empty($os[Object[@Name="Zip"]]) and $o[@Name="State"]) then
              ($o,<Object Name="Zip" Value="abcde"/>)
            else
              $o
        }
        </Objects>
        </DataObject>
      ')
FROM @mockupTable t;

Идея вкратце:

Мы перебираем все <Objects>, используя переменную $os.
Теперь мы начинаем секунду для -l oop ниже $os и перебираем все содержащиеся <Object> elements.
Идея такова: если $os НЕ получил <Object> с Name равным Zip (см. использование empty()) и текущий <Object> имеет Name of State мы собираемся вернуть элемент $o без изменений, но вместе с новым элементом для вставки Zip -элемента сразу после элемента с «State».
В любом другом случае мы просто возвращаем $o, возвращая, таким образом, все как есть .

Вы можете использовать созданное XML в простом ОБНОВЛЕНИИ и обновлять вашу таблицу одним вызовом.

ОБНОВЛЕНИЕ

Код для обновления всей таблицы за один звонок

WITH updatableCTE AS
(
    SELECT t.YourXml 
          ,t.YourXml.query
          ('
           for $os in /DataObject/Objects
           return
            <DataObject>
            <Objects>
            {
                for $o in $os/Object
                return
                if(empty($os[Object[@Name="Zip"]]) and $o[@Name="State"]) then
                  ($o,<Object Name="Zip" Value="abcde"/>)
                else
                  $o
            }
            </Objects>
            </DataObject>
          ') AS NewContent
    FROM @mockupTable t
)
UPDATE updatableCTE SET YourXml=NewContent;

--check the result
SELECT * FROM @mockupTable 
...