116

If you have a varchar field you can easily do SELECT * FROM TABLE WHERE ColumnA LIKE '%Test%' to see if that column contains a certain string.

How do you do that for XML Type?

I have the following which returns only rows that have a 'Text' node but I need to search within that node

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

5 Answers 5

144

Yet another option is to cast the XML as nvarchar, and then search for the given string as if the XML vas a nvarchar field.

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

I love this solution as it is clean, easy to remember, hard to mess up, and can be used as a part of a where clause.

This might not be the best performing solution, so think twice before deploying it to production. It is however very useful for a quick debug session, which is where I mostly use it.

EDIT: As Cliff mentions it, you could use:

...nvarchar if there's characters that don't convert to varchar

Sign up to request clarification or add additional context in comments.

1 Comment

Ditto on that, or nvarchar if there's characters that don't convert to varchar SELECT * FROM Table WHERE CAST(Column as nvarchar(max)) LIKE '%TEST%'
76

You should be able to do this quite easily:

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

The .value method gives you the actual value, and you can define that to be returned as a VARCHAR(), which you can then check with a LIKE statement.

Mind you, this isn't going to be awfully fast. So if you have certain fields in your XML that you need to inspect a lot, you could:

  • create a stored function which gets the XML and returns the value you're looking for as a VARCHAR()
  • define a new computed field on your table which calls this function, and make it a PERSISTED column

With this, you'd basically "extract" a certain portion of the XML into a computed field, make it persisted, and then you can search very efficiently on it (heck: you can even INDEX that field!).

Marc

7 Comments

I'm basically implemeting a search feature so I want to search the XML column only on the 'Text' nodes and then return a substring to indicate that the search has found a match. For example search on 'hi there' instead of returning the whole xml column I'd just return a substring such as 'the chap said hi there and carried...'
Beat me to it by 5 seconds. Another possibility is to consider using free text search, if your data is amenable...
to search the whole filed: WHERE xmlField.value('.', 'varchar(max)') LIKE '%FOO%'
watch out for pesky Xml namespaces if you get NULL back
@FMFF: if you have XML namespaces in your document, look into the WITH XMLNAMESPACES clause in T-SQL - it lets you define the XML namespaces involved, and you can use their prefixes in your T-SQL code
|
13

Another option is to search the XML as a string by converting it to a string and then using LIKE. However as a computed column can't be part of a WHERE clause you need to wrap it in another SELECT like this:

SELECT * FROM
    (SELECT *, CONVERT(varchar(MAX), [COLUMNA]) as [XMLDataString] FROM TABLE) x
WHERE [XMLDataString] like '%Test%'

1 Comment

Be advised this may bypass any selective xml indexes you may have in place and take a hit on performance.
0

This is what I am going to use based on marc_s answer:

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

Return a substring on the search where the search criteria exists

2 Comments

Do I need parameters to prevent injection somehow?
BE AWARE: those XML function ARE case-sensitive - DATA.VALUE will not work ! It needs be to .value(...)
0

The correct way to do this is to use the contains() XQuery predicate

SELECT *
FROM WebPageContent wpc
WHERE wpc.data.exist('/PageContent/Text/text()[contains(., "Test")]') = 1;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.