Используйте оператор LIKE для типа данных XML SQL Server

87

Если у вас есть поле varchar, вы можете легко SELECT * FROM TABLE WHERE ColumnA LIKE '%Test%'проверить, содержит ли этот столбец определенную строку.

Как это сделать для типа XML?

У меня есть следующее, которое возвращает только строки с узлом "Текст", но мне нужно искать в этом узле

select * from WebPageContent where data.exist('/PageContent/Text') = 1
Джон
источник

Ответы:

70

У вас должно получиться это довольно легко:

SELECT * 
FROM WebPageContent 
WHERE data.value('(/PageContent/Text)[1]', 'varchar(100)') LIKE 'XYZ%'

.valueМетод дает фактическое значение, и вы можете определить , что должно быть возвращено как VARCHAR (), который вы можете проверить с заявлением LIKE.

Имейте в виду, это не будет очень быстро. Итак, если у вас есть определенные поля в вашем XML, которые вам нужно много проверять, вы можете:

  • создать сохраненную функцию, которая получает XML и возвращает искомое значение в виде VARCHAR ()
  • определите новое вычисляемое поле в вашей таблице, которое вызывает эту функцию, и сделайте его PERSISTED столбцом

При этом вы в основном «извлекаете» определенную часть XML в вычисляемое поле, сохраняете ее, а затем можете очень эффективно искать по ней (черт возьми: вы даже можете ИНДЕКСИРОВАТЬ это поле!).

Марк

marc_s
источник
1
Я в основном реализую функцию поиска, поэтому я хочу искать столбец XML только на узлах «Текст», а затем возвращать подстроку, чтобы указать, что поиск нашел совпадение. Например, поиск по «привет там» вместо того, чтобы возвращать весь столбец xml, я бы просто вернул подстроку, такую ​​как «парень сказал привет и понес ...»
Джон
1
Обгони меня на 5 секунд. Другая возможность - рассмотреть возможность использования свободного текстового поиска, если ваши данные доступны ...
RickNZ
10
для поиска по всему полю: WHERE xmlField.value ('.', 'varchar (max)') LIKE '% FOO%'
jhilden
остерегайтесь надоедливых пространств имен Xml, если вы
вернете
87

Еще один вариант - преобразовать XML как nvarchar, а затем выполнить поиск данной строки, как если бы XML был полем nvarchar.

SELECT * 
FROM Table
WHERE CAST(Column as nvarchar(max)) LIKE '%TEST%'

Мне нравится это решение, поскольку оно чистое, легко запоминающееся, его трудно испортить и его можно использовать как часть предложения where.

РЕДАКТИРОВАТЬ: как упоминает Клифф, вы можете использовать:

... nvarchar, если есть символы, которые не конвертируются в varchar

Squazz
источник
3
То же самое или nvarchar, если есть символы, которые не конвертируются в varchar SELECT * FROM Table WHERE CAST (Column as nvarchar (max)) LIKE '% TEST%'
Cliff Coulter
[Err] 42000 - [SQL Server] Преобразование одного или нескольких символов из XML в целевое сопоставление невозможно
digz6666
[Err] 22018 - [SQL Server] Явное преобразование типа данных xml в текст не допускается.
digz6666
Похоже, вы делаете что-то не так @ digz6666
Squazz
1
@Squazz Последний раз вы голосовали за этот ответ вчера. Ваш голос теперь заблокирован, если этот ответ не будет отредактирован. :)
digz6666
10

Другой вариант - искать XML как строку, преобразовав ее в строку, а затем используя LIKE. Однако, поскольку вычисляемый столбец не может быть частью предложения WHERE, вам необходимо обернуть его в другой SELECT следующим образом:

SELECT * FROM
    (SELECT *, CONVERT(varchar(MAX), [COLUMNA]) as [XMLDataString] FROM TABLE) x
WHERE [XMLDataString] like '%Test%'
Карл Онагер
источник
Имейте в виду, что это может обойти любые избирательные XML-индексы, которые могут быть у вас на месте, и снизить производительность.
Руди Инохоса
0

Это то, что я собираюсь использовать на основе ответа marc_s:

SELECT 
SUBSTRING(DATA.value('(/PAGECONTENT/TEXT)[1]', 'VARCHAR(100)'),PATINDEX('%NORTH%',DATA.value('(/PAGECONTENT/TEXT)[1]', 'VARCHAR(100)')) - 20,999)

FROM WEBPAGECONTENT 
WHERE COALESCE(PATINDEX('%NORTH%',DATA.value('(/PAGECONTENT/TEXT)[1]', 'VARCHAR(100)')),0) > 0

Возвратите подстроку поиска, в которой существует критерий поиска

Джон
источник
Мне нужны параметры, чтобы как-то предотвратить инъекцию?
Джон
2
ВНИМАНИЕ: эти XML-функции чувствительны к регистру - DATA.VALUE работать не будет ! Это должно быть .value (...)
marc_s