Utiliser une instruction LIKE sur le type de données XML SQL Server

87

Si vous avez un champ varchar, vous pouvez facilement le faire SELECT * FROM TABLE WHERE ColumnA LIKE '%Test%'pour voir si cette colonne contient une certaine chaîne.

Comment faites-vous cela pour le type XML?

J'ai ce qui suit qui ne renvoie que les lignes qui ont un nœud 'Texte' mais j'ai besoin de rechercher dans ce nœud

select * from WebPageContent where data.exist('/PageContent/Text') = 1
Jon
la source

Réponses:

70

Vous devriez pouvoir le faire assez facilement:

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

La .valueméthode vous donne la valeur réelle, et vous pouvez la définir pour qu'elle soit renvoyée comme un VARCHAR (), que vous pouvez ensuite vérifier avec une instruction LIKE.

Attention, cela ne va pas être très rapide. Donc, si vous avez certains champs dans votre XML que vous devez inspecter beaucoup, vous pouvez:

  • créer une fonction stockée qui récupère le XML et renvoie la valeur que vous recherchez en tant que VARCHAR ()
  • définir un nouveau champ calculé sur votre table qui appelle cette fonction, et en faire une colonne PERSISTÉE

Avec cela, vous "extrairez" fondamentalement une certaine partie du XML dans un champ calculé, le rendriez persistant, et vous pourrez alors rechercher très efficacement dessus (diable: vous pouvez même INDEXER ce champ!).

Marc

marc_s
la source
1
J'implémente essentiellement une fonction de recherche, donc je veux rechercher la colonne XML uniquement sur les nœuds 'Texte', puis renvoyer une sous-chaîne pour indiquer que la recherche a trouvé une correspondance. Par exemple, effectuez une recherche sur `` salut là-bas '' au lieu de renvoyer toute la colonne xml, je retournerais simplement une sous-chaîne telle que `` le gars a dit bonjour et porté ... ''
Jon
1
Battez-moi de 5 secondes. Une autre possibilité est d'envisager d'utiliser la recherche de texte libre, si vos données sont
acceptables
10
pour rechercher l'ensemble du fichier: WHERE xmlField.value ('.', 'varchar (max)') LIKE '% FOO%'
jhilden
attention aux espaces de noms Xml embêtants si vous récupérez NULL
Simon_Weaver
87

Une autre option encore consiste à convertir le XML en nvarchar, puis à rechercher la chaîne donnée comme si le XML était un champ nvarchar.

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

J'adore cette solution car elle est propre, facile à retenir, difficile à gâcher et peut être utilisée dans le cadre d'une clause where.

EDIT: Comme Cliff le mentionne, vous pouvez utiliser:

... nvarchar s'il y a des caractères qui ne se convertissent pas en varchar

Squazz
la source
3
Idem là-dessus, ou nvarchar s'il y a des caractères qui ne se convertissent pas en varchar SELECT * FROM Table WHERE CAST (Column as nvarchar (max)) LIKE '% TEST%'
Cliff Coulter
[Err] 42000 - [SQL Server] Conversion d'un ou plusieurs caractères de XML vers le classement cible impossible
digz6666
[Err] 22018 - [SQL Server] La conversion explicite du type de données xml en texte n'est pas autorisée.
digz6666
On dirait que vous faites quelque chose de mal @ digz6666
Squazz
1
@Squazz Vous avez voté pour la dernière fois sur cette réponse hier. Votre vote est désormais verrouillé à moins que cette réponse ne soit modifiée. :)
digz6666
10

Une autre option consiste à rechercher le XML sous forme de chaîne en le convertissant en chaîne, puis en utilisant LIKE. Cependant, comme une colonne calculée ne peut pas faire partie d'une clause WHERE, vous devez l'envelopper dans un autre SELECT comme ceci:

SELECT * FROM
    (SELECT *, CONVERT(varchar(MAX), [COLUMNA]) as [XMLDataString] FROM TABLE) x
WHERE [XMLDataString] like '%Test%'
Carl Onager
la source
Sachez que cela peut contourner tous les index XML sélectifs que vous pourriez avoir en place et nuire aux performances.
Rudy Hinojosa
0

Voici ce que je vais utiliser en fonction de la réponse de 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

Renvoie une sous-chaîne sur la recherche où les critères de recherche existent

Jon
la source
Ai-je besoin de paramètres pour empêcher l'injection d'une manière ou d'une autre?
Jon
2
ATTENTION: ces fonctions XML SONT sensibles à la casse - DATA.VALUE ne fonctionnera pas ! Il faut .value (...)
marc_s