Лучшая практика:
- ТОЛЬКО обновляйте XML / записи, которые требуют обновления.
(т.е. избегать обновления всех записей независимо от того, отличаются ли они данными).
- ТОЛЬКО обновляйте XML / записи один раз.
(Например, не удаляйте каждый узел, только чтобы потом вернуться и вставить каждый узел).
- Повышение производительности, оказывающее минимальное влияние.
(т. е. избегайте вставки или удаления узлов, когда подойдет простая замена или вставка текста.
- Не рискуйте потерять другие важные данные.
(То есть НИКОГДА не удаляйте узел и рискуйте потерять возможные дочерние элементы, когда все, что вы делаете, это обновляет текстовое значение).
- Создайте что-то повторно используемое, которое будет работать для каждого сценария.
(т. е. вопрос задает нам, как это сделать, когда вы уже знаете уникальный идентификатор.
Однако разработайте свой ответ так, чтобы он обрабатывал несколько записей наиболее эффективным способом).
XML-столбец
Вот как я мог бы написать его для обновления поля XML в таблице:
DECLARE @newValue nVarChar(128) = '01'
--Insert a Value when the Element is Empty (i.e. <AgencyID />), so it becomes <AgencyID>01<\AgencyID>.
UPDATE dbo.UploadReport
SET XmlTest.modify('insert text{sql:variable("@newValue")} as first into (/CodeFiveReport/Owner/AgencyID)[1]')
WHERE XmlTest.value('(/CodeFiveReport/Owner/AgencyID)[1]', 'nVarChar(128)') = ''--Node is: <AgencyID />
AND id = 'myId'
--Replace the Value if Text already Exists AND is Different (e.g. <AgencyID>99<\AgencyID>).
-- Note: This will not work for Empty-Elements (i.e. <AgencyID />), which is why we perform the Update Above.
UPDATE dbo.UploadReport
SET XmlTest.modify('replace value of (/CodeFiveReport/Owner/AgencyID)[1] with sql:variable("@newValue")')
WHERE XmlTest.value('(/CodeFiveReport/Owner/AgencyID)[1]', 'nVarChar(128)') != @newValue--Node is like: <AgencyID>99<\AgencyID>
AND id = 'myId'
--Optional. Use the Update below if it is possible for an Element to not exist at all.
UPDATE dbo.UploadReport
SET XmlTest.modify('insert <AgencyID>{sql:variable("@newValue")}</AgencyID> as first into (/CodeFiveReport/Owner)[1]')
WHERE XmlTest.exist('/CodeFiveReport/Owner/AgencyID') = 0--The AgencyID Element/Node is missing entirely.
AND id = 'myId'
--AND XmlTest.value('(/CodeFiveReport/Owner/AgencyID)[1]', 'nVarChar(128)') IS NULL--Same thing as Exist(), only without the overhead of Casting.
Переменная XML
Если вы хотите обновить только переменную XML (а не поле XML в таблице), я бы использовал этот подход.
Я предпочитаю это, потому что вы не удаляете существующий узел или добавляете его без необходимости (который я бы представил медленнее). Вы обновляете его только тогда, когда это абсолютно необходимо.
К сведению: элемент может иметь текстовое значение и другие дочерние элементы - это разрешено спецификацией XML.
DECLARE @Xml Xml = N'<Root><Note /></Root>'--Works for: "<Root></Root>", "<Root><Note /></Root>", and "<Root><Note>Something</Note></Root>".
DECLARE @Note NVarChar(128) = 'Hello'
IF(@Xml.value('(/Root/Note)[1]', 'nVarChar(128)') = '') SET @Xml.modify('insert text{sql:variable("@Note")} as first into (/Root/Note)[1]') --Node is: <Note />
IF(@Xml.value('(/Root/Note)[1]', 'nVarChar(128)') != @Note) SET @Xml.modify('replace value of (/Root/Note/text())[1] with sql:variable("@Note")') --Node is like: <Note>Something<\Note>
IF(@Xml.exist('/Root/Note') = 0) SET @Xml.modify('insert <Note>{sql:variable("@Note")}</Note> as first into (/Root)[1]')--Node is missing: <Root></Root>
SELECT @Xml[@Xml]