Обновление столбца типа XML с условием
Надеюсь, быстрый вопрос, на который должны ответить эксперты XML. У меня есть следующая таблица со столбцом типа XML с приведенной ниже схемой XML и данными в одной записи:
INSERT INTO XMLTEST (testXML)
values ('<R>
<P N="Status" V="
Draft
" />
<P N="Approved For Publishing" V="
T
" />
<P N="Concealed From Publishing" V="
F
" />
<P N="Management System" V="
OMS
BMS
" />
</R>')
Мне нужно условно заменить значение "Approved For Publishing" на F, и я подумал, что лучше всего сначала проверить соответствие имени N в строке xml, прежде чем изменять значение V. Я пытался заставить работать приведенный ниже SQL, но не повезло. Может кто-нибудь помочь указать, что не так?
DECLARE @myDoc xml
SET @myDoc = (SELECT TOP 1 CAST(testXML AS VARCHAR(MAX)) FROM XMLTEST)
Select @myDoc
SET @myDoc.modify('
replace value of (/R/P/@V)[1]
with (
if (/R/P/N = ''Approved For Publishing'') then
"F"
else
"T"
)
')
SELECT @myDoc
2 ответа
В конце концов, нижеследующее закончилось работой, основываясь на отзывах Шнуго, Спасибо! Это было огромной помощью для меня, чтобы понять XQUERY немного лучше!!
Мне нужно было обновить содержимое столбца типа XML в SQL, и ниже приведена основная рабочая часть, необходимая как часть общего решения для массового изменения значений в старой унаследованной системе, с которой я работаю. Я уверен, что есть более элегантное решение, но мне пришлось установить переменную со специальными символами xml, чтобы сохранить строковый литерал в самом столбце - по какой-то причине я не смог заставить его работать в строке после 'then' часть блока if.
Я также изменяю его так, чтобы он только изменял значение, попадающее в условную проверку, и сохранял те же данные для остальных.
DECLARE @myDoc xml
SET @myDoc = (SELECT TOP 1 testXML FROM XMLTEST)
UPDATE XMLTEST
SET testXML = (SELECT @myDoc.query(
N'
<R>
{
let $str := "
F
"
for $p in /R/P
return
if($p/@N="Approved For Publishing") then
<P N="{$p/@N}" V="{$str}"/>
else
<P N="{$p/@N}" V="{$p/@V}"/>
}
</R>
'))
SELECT * FROM XMLTEST
Вывод XML после того, где значение "Approved For Publishing" установлено в "F" из "T", сохраняя специальные символы XML.
<R>
<P N="Status" V="
Draft
" />
<P N="Approved For Publishing" V="
F
" />
<P N="Concealed From Publishing" V="
F
" />
<P N="Management System" V="
OMS
BMS
" />
</R>
Новый ответ
Специальные символы XML 

а также
это не что иное, как новая строка (перевод строки (LF) и возврат каретки (CR)). Я не думаю, что это должно действительно соответствовать ценности XML. Перевод строки должен быть скорее добавлен в ваш уровень представления.
В следующем коде показано, как обновить таблицу без необходимости использования переменной или приведения.
DECLARE @tbl TABLE(ID INT IDENTITY, testXML XML);
INSERT INTO @tbl (testXML)
values ('<R>
<P N="Status" V="
Draft
" />
<P N="Approved For Publishing" V="
T
" />
<P N="Concealed From Publishing" V="
F
" />
<P N="Management System" V="
OMS
BMS
" />
</R>')
,('<R>
<P N="Status" V="
Draft
" />
<P N="Approved For Publishing" V="
T
" />
<P N="Concealed From Publishing" V="
F
" />
<P N="Management System" V="
OMS
BMS
" />
</R>');
UPDATE @tbl SET testXML=testXML.query(
N'
<R>
{
for $p in /R/P
return
if($p/@N="Approved For Publishing") then
<P N="{$p/@N}" V="F"/>
else
<P N="{$p/@N}" V="{substring($p/@V,3,string-length($p/@V)-4)}"/>
}
</R>
');
SELECT * FROM @tbl;
Один из XML будет выглядеть так после действия:
<R>
<P N="Status" V="Draft" />
<P N="Approved For Publishing" V="F" />
<P N="Concealed From Publishing" V="F" />
<P N="Management System" V="OMS
BMS" />
</R>
Новая линия между OMS
а также BMS
остается в этом подходе.
Предыдущий ответ
Некоторые замечания / вопросы в первую очередь?
- Зачем вам нужно бросить это
VARCHAR(MAX)
а затемXML
(Неявно)? - Вам нужно обновить значения в таблице или это временное действие?
- Вы не можете обновить более одного значения одновременно
.modify()
... - Вы должны всегда использовать
NVARCHAR
если вы имеете дело с XML! - Есть ли необходимость в переменной
@myDoc
?
Вы можете попробовать это с FLWOR XQuery
:
DECLARE @tbl TABLE(testXML XML);
INSERT INTO @tbl (testXML)
values ('<R>
<P N="Status" V="
Draft
" />
<P N="Approved For Publishing" V="
T
" />
<P N="Concealed From Publishing" V="
F
" />
<P N="Management System" V="
OMS
BMS
" />
</R>');
DECLARE @myDoc xml
SET @myDoc = (SELECT TOP 1 CAST(testXML AS VARCHAR(MAX)) FROM @tbl)
SELECT @myDoc.query(
N'
<R>
{
for $p in /R/P
return
if($p/@N="Approved For Publishing") then
<P N="{$p/@N}" V="F"/>
else
<P N="{$p/@N}" V="T"/>
}
</R>
')
Результат
<R>
<P N="Status" V="T" />
<P N="Approved For Publishing" V="F" />
<P N="Concealed From Publishing" V="T" />
<P N="Management System" V="T" />
</R>