Проверка определенного значения узла в столбце XML, включая потенциально пустые узлы

У меня есть столбец XML в базе данных SQL Server 2008 со значениями, подобными следующим упрощенным примерам:

Случай 1

<root>
    <child>sometimes text here</child>
    <otherstuff ..... />
</root>

Случай 2

<root>
    <child/>
    <otherstuff ..... />
</root>

Учитывая строковое значение, я хотел бы иметь возможность выбирать строки, которые имеют определенное значение в «дочернем» узле, включая выбор случая 2.

Так, например, если у меня есть локальная переменная:

declare @queryText nvarchar(MAX)
select @queryText = 'sometimes text here'

Я могу выбрать строку, соответствующую случаю 1, следующим образом:

select * from [my_table] 
where [my_xml_column].exist('/root/child[text()=sql:variable("@queryText")]') = 1

Однако для случая 2, когда я ожидаю, что @queryText = '' или @queryText = NULL будут работать, ни одно из них не соответствует.

В качестве обходного пути я могу использовать:

select * from [my_table]
where [my_xml_column].value('(/root/child)[1], 'nvarchar(MAX)') = @queryText

Это работает, но у меня возникает ощущение, что я что-то упускаю и использую грязный обходной путь для проверки существования с помощью .value(), а не .exist()... Есть ли подобное выражение, которое я могу [и должен?] использовать в .exist() для соответствия либо конкретному тексту, либо пустому узлу? Есть ли какая-то причина для беспокойства помимо читабельности? Я с нетерпением жду моего предстоящего фейспалма, когда кто-нибудь укажет на какую-то очевидную вещь, которую я пропустил. :)


person Rick Riensche    schedule 21.06.2012    source источник


Ответы (1)


Вызов text() для пустого элемента приводит к NULL, а не к пустой строке. Таким образом, в обоих случаях передача @queryText = '' или @queryText = NULL никогда не будет равна NULL. Помните, что ничто не равно NULL, даже NULL.

См. ниже пример, который иллюстрирует, как использовать exist для заполненных или пустых поисков.

declare @my_table table (i int, my_xml_column xml)
insert into @my_table
    select 1, '<root><child>sometimes text here</child><otherstuff /></root>' union all
    select 2, '<root><child/><otherstuff/></root>'

declare @queryText varchar(100) = '';

select  *, 
        [using_text()]=[my_xml_column].value('(/root/child/text())[1]', 'varchar(max)'),
        [using_path]=[my_xml_column].value('(/root/child)[1]', 'varchar(max)')
from    @my_table

select  *
from    @my_table
where   [my_xml_column].exist('/root/child[.= sql:variable("@queryText")]') = 1
person Nathan Skerl    schedule 22.06.2012
comment
Давай фейспалм! :) Я мог бы поклясться, что пробовал эту форму [.=sql:variable], но, похоже, нет. Работает как шарм - спасибо! - person Rick Riensche; 22.06.2012
comment
Как насчет чего-то вроде этого: stackoverflow.com/questions/33130499/ - person Si8; 14.10.2015