Utilice una declaración LIKE en el tipo de datos XML de SQL Server

87

Si tiene un campo varchar, puede hacerlo fácilmente SELECT * FROM TABLE WHERE ColumnA LIKE '%Test%'para ver si esa columna contiene una determinada cadena.

¿Cómo se hace eso para XML Type?

Tengo lo siguiente que devuelve solo filas que tienen un nodo 'Texto' pero necesito buscar dentro de ese nodo

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

Respuestas:

70

Debería poder hacer esto con bastante facilidad:

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

El .valuemétodo le da el valor real y puede definir que se devuelva como VARCHAR (), que luego puede verificar con una declaración LIKE.

Eso sí, esto no va a ser demasiado rápido. Entonces, si tiene ciertos campos en su XML que necesita inspeccionar mucho, podría:

  • cree una función almacenada que obtenga el XML y devuelva el valor que está buscando como VARCHAR ()
  • defina un nuevo campo calculado en su tabla que llame a esta función y conviértalo en una columna PERSISTED

Con esto, básicamente "extraerías" una cierta porción del XML en un campo calculado, harías que persistiera, y luego podrás buscar de manera muy eficiente en él (diablos: ¡incluso puedes INDEX ese campo!).

Bagazo

marc_s
fuente
1
Básicamente, estoy implementando una función de búsqueda, por lo que quiero buscar en la columna XML solo en los nodos 'Texto' y luego devolver una subcadena para indicar que la búsqueda ha encontrado una coincidencia. Por ejemplo, busque 'hola' en lugar de devolver toda la columna xml, solo devolvería una subcadena como 'el tipo dijo hola allí y llevó ...'
Jon
1
Gáname por 5 segundos. Otra posibilidad es considerar el uso de la búsqueda de texto libre, si sus datos son
compatibles
10
para buscar todo el archivo: DONDE xmlField.value ('.', 'varchar (max)') LIKE '% FOO%'
jhilden
tenga cuidado con los molestos espacios de nombres XML si recupera NULL
Simon_Weaver
87

Otra opción más es convertir el XML como nvarchar y luego buscar la cadena dada como si el XML fuera un campo nvarchar.

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

Me encanta esta solución porque es limpia, fácil de recordar, difícil de estropear y se puede utilizar como parte de una cláusula where.

EDITAR: Como lo menciona Cliff, podría usar:

... nvarchar si hay caracteres que no se convierten a varchar

Squazz
fuente
3
Lo mismo ocurre con eso, o nvarchar si hay caracteres que no se convierten a varchar SELECT * FROM Table WHERE CAST (Column as nvarchar (max)) LIKE '% TEST%'
Cliff Coulter
[Err] 42000 - [SQL Server] Conversión de uno o más caracteres de XML a intercalación de destino imposible
digz6666
[Err] 22018 - [SQL Server] No se permite la conversión explícita del tipo de datos xml a texto.
digz6666
Parece que estás haciendo algo mal @ digz6666
Squazz
1
@Squazz Votó por última vez esta respuesta ayer. Su voto ahora está bloqueado a menos que se edite esta respuesta. :)
digz6666
10

Otra opción es buscar el XML como una cadena convirtiéndolo en una cadena y luego usando LIKE. Sin embargo, como una columna calculada no puede ser parte de una cláusula WHERE, debe envolverla en otro SELECT como este:

SELECT * FROM
    (SELECT *, CONVERT(varchar(MAX), [COLUMNA]) as [XMLDataString] FROM TABLE) x
WHERE [XMLDataString] like '%Test%'
Carl Onager
fuente
Tenga en cuenta que esto puede eludir cualquier índice xml selectivo que pueda tener y afectar el rendimiento.
Rudy Hinojosa
0

Esto es lo que voy a usar según la respuesta 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

Devuelve una subcadena en la búsqueda donde existe el criterio de búsqueda

Jon
fuente
¿Necesito parámetros para evitar la inyección de alguna manera?
Jon
2
TENGA EN CUENTA: esas funciones XML distinguen entre mayúsculas y minúsculas: ¡DATA.VALUE no funcionará! Debe ser .value (...)
marc_s