Как обновить узел XML с использованием XML DML в SQL Server - PullRequest
1 голос
/ 07 ноября 2019

Я хочу обновить переменную XML в SQL Server 2012.

Пример XML:

<ArrayOfFieldInfo>
    <FieldInfo>
        <Name>abc</Name>
        <FriendlyName>friend2</FriendlyName>
        <Description>Custom added field</Description>
        <FieldGroupName>Custom Fields</FieldGroupName>
    </FieldInfo>
    <FieldInfo>
        <Name>efg</Name>
        <FriendlyName>friend1</FriendlyName>
        <Description>Custom added field</Description>
        <FieldGroupName>Custom Fields</FieldGroupName>
    </FieldInfo>
</ArrayOfFieldInfo>

Я хочу обновить узел <FriendlyName> с новым значением Friend3, где <Name> значение узла равно 'efg'.

Я пробовал следующий код, но он не работает.

DECLARE @xmlTable table (xmlfield xml)

INSERT INTO @xmlTable (xmlfield) 
VALUES (
    '<ArrayOfFieldInfo>
        <FieldInfo>
            <Name>abc</Name>
            <FriendlyName>friend2</FriendlyName>
            <Description>Custom added field</Description>
            <FieldGroupName>Custom Fields</FieldGroupName>
        </FieldInfo>
        <FieldInfo>
            <Name>efg</Name>
            <FriendlyName>friend1</FriendlyName>
            <Description>Custom added field</Description>
            <FieldGroupName>Custom Fields</FieldGroupName>
        </FieldInfo>
    </ArrayOfFieldInfo>'
)


UPDATE @xmlTable
SET xmlfield.modify('replace value of (/ArrayOfFieldInfo/FieldInfo/FriendlyName/text())[1] with "friend3"')
WHERE xmlfield.value('(/ArrayOfFieldInfo/FieldInfo/Name/text())[1]', 'nvarchar(50)') = 'efg'

1 Ответ

1 голос
/ 07 ноября 2019

Да, но вы должны добавить фильтр в качестве предиката:

Ваш макет . Я добавил столбец идентификатора и три случая

  1. Один "efg"
  2. Нет "efg"
  3. Множественные вхождения "efg"

- Проверить это

DECLARE @xmlTable table (ID INT IDENTITY, xmlfield xml)

INSERT INTO @xmlTable (xmlfield) 
VALUES (
    '<ArrayOfFieldInfo>
        <FieldInfo>
            <Name>abc</Name>
            <FriendlyName>friend2</FriendlyName>
        </FieldInfo>
        <FieldInfo>
            <Name>efg</Name>
            <FriendlyName>friend1</FriendlyName>
        </FieldInfo>
    </ArrayOfFieldInfo>'
)
,(
    '<ArrayOfFieldInfo>
        <FieldInfo>
            <Name>abc</Name>
            <FriendlyName>friend2</FriendlyName>
        </FieldInfo>
        <FieldInfo>
            <Name>xyz</Name>
            <FriendlyName>friend1</FriendlyName>
        </FieldInfo>
    </ArrayOfFieldInfo>'
)
,(
    '<ArrayOfFieldInfo>
        <FieldInfo>
            <Name>efg</Name>
            <FriendlyName>friend2</FriendlyName>
        </FieldInfo>
        <FieldInfo>
            <Name>efg</Name>
            <FriendlyName>friend1</FriendlyName>
        </FieldInfo>
    </ArrayOfFieldInfo>'
)

- Давайте использовать некоторые переменные, чтобы получить более общий

DECLARE @SearchName NVARCHAR(100)=N'efg';
DECLARE @ReplaceWith NVARCHAR(100)=N'ABCDEFG';

- Запрос

UPDATE @xmlTable
SET xmlfield.modify('replace value of 
                     (/ArrayOfFieldInfo
                      /FieldInfo[Name=sql:variable("@SearchName")]
                      /FriendlyName/text())[1] 
                     with sql:variable("@ReplaceWith")')
WHERE xmlfield.exist('/ArrayOfFieldInfo
                      /FieldInfo[Name=sql:variable("@SearchName")]')=1;

--Проверьте результат

SELECT * 
FROM @xmlTable t
ORDER BY t.ID;

В первом случае будет заменено значение.
Во втором случае нет замен.
Только ОДИН (первый) в третьем случае заменяется.

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

XML-DML-метод replace value of нуждается в одиночном коде. Вы пытались отфильтровать с помощью WHERE извне, но вы должны указать движку , какой узел в XML вы хотите адресовать. Это <FieldInfo>, где целое число <Name> равно вашей поисковой фразе. Ниже этого <FieldInfo> мы выбираем <FriendlyName> и заменяем первый найденный нами.

Я добавил WHERE, используя .exist(). Это ускорит / может ускорить процесс, поскольку сократит число .modify() до строк, в которых есть хотя бы один подходящий целевой элемент.

И вы можете видеть, что .modify() ограничено только одним изменением ввызов. Это может быть очень раздражающим ... Обходным путем может быть уничтожение и воссоздание XML или использование XQuery-FLWOR.

...