SQL: удалить узел XML в инструкции UPDATE - PullRequest
1 голос
/ 05 марта 2020

Я получаю эту ошибку:

Incorrect use of the XML data type method 'modify'. A non-mutator method is expected in this context.

В поисках решения этого ответа я наткнулся на эту статью об обновлении в инструкции SELECT: Как мне выполнить ОБНОВЛЕНИЕ из SELECT в SQL Сервер?

Пример ... Примечание: поле CustomProperties - это nvarchar (max), которое я конвертирую в xml.

<CustomProperties
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldA</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldB</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldC</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldD</Key>
        <Value>....</Value>
    </CustomProperty>
</CustomProperties>


DECLARE @myVar varchar(50) = 'FieldA';

UPDATE Table_1
SET 
    Table_1.CustomProperties = CONVERT(xml, Table_2.CustomProperties).modify('delete (/CustomProperties/CustomProperty[Key = sql:variable("@myVar")])')
FROM
    [dbo].MyTable AS Table_1
    INNER JOIN [dbo].MyTable AS Table_2 on Table_1.id = Table_1.id
WHERE
    // shortened for brevity

Я также пытался курсор (неприятные вещи), но получая ту же ошибку.

Есть ли способ, которым я могу сделать общее обновление всех строк. Моя цель - удалить один узел из CustomProperties XML, чтобы указать c данные строки в этой таблице.

Ответы [ 2 ]

1 голос
/ 06 марта 2020

Вам уже говорили, что хранить XML в строковом типе - очень плохая идея. Всегда лучший выбор - использовать соответствующий тип в вашем дизайне данных.

Кроме того, если вам нужно придерживаться этого дизайна (иногда мы должны делать причудливые вещи), вы можете попробовать что-то подобное:

- Создание макета таблицы для имитации вашей проблемы:

DECLARE @tbl TABLE(CustomProperties VARCHAR(1000));
INSERT INTO @tbl VALUES
('<CustomProperties
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldA</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldB</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldC</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldD</Key>
        <Value>....</Value>
    </CustomProperty>
</CustomProperties>');

- строка поиска

DECLARE @myVar varchar(50) = 'FieldA';

- запрос

UPDATE @tbl SET CustomProperties = CAST(CAST(CustomProperties AS XML)
                                   .query('
                                            <CustomProperties>
                                            {/CustomProperties/CustomProperty[Key != sql:variable("@myVar")]}
                                            </CustomProperties>
                                         ') AS VARCHAR(1000));
SELECT * FROM @tbl;

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

Вместо XMLDML (через .modify()) мы используем простой UPDATE ... SET ... и присваиваем воссозданному XML. Метод XML .query() вернет XML без значения <CustomProperty>, соответствующего предикату.

1 голос
/ 06 марта 2020

Дело в том, что вы не можете сделать SET CustomProperties = CONVERT(xml, CustomProperties).modify(...), потому что вы не можете объединить операции DML и XMLDML в одном выражении.

Другими словами, столбец CustomProperties должен фактически быть xml sql тип, а не nvarchar(max) или что-либо еще. В этом случае ваш XML оператор удаления будет успешным:

select [CustomProperties] = cast('<CustomProperties
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldA</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldB</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldC</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldD</Key>
        <Value>....</Value>
    </CustomProperty>
</CustomProperties>' as xml)
into #tempTable;

declare @myVar varchar(50) = 'FieldA';

update #tempTable
set [CustomProperties].modify('delete (/CustomProperties/CustomProperty[Key = sql:variable("@myVar")])')

select * from #tempTable

, что приводит к:

<CustomProperties xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldB</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldC</Key>
        <Value>....</Value>
    </CustomProperty>
    <CustomProperty>
        <Dev>....</Dev>
        <Key>FieldD</Key>
        <Value>....</Value>
    </CustomProperty>
</CustomProperties>
...