Use uma instrução LIKE no tipo de dados XML do SQL Server

87

Se você tiver um campo varchar, poderá facilmente SELECT * FROM TABLE WHERE ColumnA LIKE '%Test%'verificar se essa coluna contém uma determinada string.

Como você faz isso para o tipo XML?

Eu tenho o seguinte, que retorna apenas as linhas que têm um nó 'Texto', mas preciso pesquisar dentro desse nó

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

Respostas:

70

Você deve ser capaz de fazer isso facilmente:

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

O .valuemétodo fornece o valor real e você pode defini-lo para ser retornado como um VARCHAR (), que você pode verificar com uma instrução LIKE.

Veja bem, isso não vai ser muito rápido. Portanto, se você tiver determinados campos em seu XML que precise inspecionar muito, poderá:

  • crie uma função armazenada que obtém o XML e retorna o valor que você está procurando como um VARCHAR ()
  • definir um novo campo computado em sua tabela que chama esta função, e torná-lo uma coluna PERSISTED

Com isso, você basicamente "extrairia" uma determinada parte do XML em um campo computado, tornaria-o persistente e, então, poderia pesquisar com muita eficiência (diabos: você pode até INDEXAR esse campo!).

Marc

marc_s
fonte
1
Basicamente, estou implementando um recurso de pesquisa, portanto, desejo pesquisar a coluna XML apenas nos nós 'Texto' e, em seguida, retornar uma substring para indicar que a pesquisa encontrou uma correspondência. Por exemplo, pesquise 'oi lá' em vez de retornar a coluna inteira xml. Eu apenas retornaria uma substring como 'o cara disse oi e carregou ...'
Jon
1
Chegue 5 segundos antes de mim. Outra possibilidade é considerar o uso de pesquisa de texto livre, se seus dados estiverem acessíveis ...
RickNZ
10
para pesquisar todo o campo: WHERE xmlField.value ('.', 'varchar (max)') LIKE '% FOO%'
jhilden
cuidado com os incômodos namespaces Xml se você receber NULL de volta
Simon_Weaver
87

Outra opção é converter o XML como nvarchar e, em seguida, pesquisar a string fornecida como se o XML fosse um campo nvarchar.

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

Adoro esta solução porque é limpa, fácil de lembrar, difícil de bagunçar e pode ser usada como parte de uma cláusula where.

EDIT: Como Cliff menciona, você pode usar:

... nvarchar se houver caracteres que não são convertidos em varchar

Squazz
fonte
3
Idem, ou nvarchar se houver caracteres que não são convertidos em varchar SELECT * FROM Table WHERE CAST (coluna como nvarchar (max)) LIKE '% TEST%'
Cliff Coulter
[Err] 42000 - [SQL Server] Conversão de um ou mais caracteres de XML para agrupamento de destino impossível
digz6666
[Err] 22018 - [SQL Server] A conversão explícita do tipo de dados xml em texto não é permitida.
digz6666
Parece que você está fazendo algo errado @ digz6666
Squazz
1
@Squazz Você votou nesta resposta pela última vez ontem. Seu voto agora está bloqueado, a menos que esta resposta seja editada. :)
digz6666
10

Outra opção é pesquisar o XML como string, convertendo-o em string e usando LIKE. No entanto, como uma coluna computada não pode fazer parte de uma cláusula WHERE, você precisa envolvê-la em outro SELECT como este:

SELECT * FROM
    (SELECT *, CONVERT(varchar(MAX), [COLUMNA]) as [XMLDataString] FROM TABLE) x
WHERE [XMLDataString] like '%Test%'
Carl Onager
fonte
Esteja ciente de que isso pode ignorar quaisquer índices xml seletivos que você possa ter em vigor e prejudicar o desempenho.
Rudy Hinojosa de
0

Isso é o que vou usar com base na resposta 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

Retorna uma substring na pesquisa onde os critérios de pesquisa existem

Jon
fonte
Preciso de parâmetros para evitar a injeção de alguma forma?
Jon
2
ATENÇÃO: essas funções XML diferenciam maiúsculas de minúsculas - DATA.VALUE não funcionará! Precisa ser .valor (...)
marc_s