How do I query XML stored as text? - sql

I have a table in a SQL Server 2005 database that logs purchases like so:
ID (PK, int, not null)
Request (text, null)
Response (text, null)
MerchantId (varchar(14), null)
Amount (money, null)
The Request and Response fields are really storing XML. I can't change the data type to XML. I need to draw a query that will get data out of the 2 text-as-XML fields in addition to data that is in the table itself.
I'm not sure where to start with this. Most of my searches come back with questions about LINQ-to-SQL, and the SQLXML results I get don't seem to be able to handle sets of data. Where should I be focusing my search?

Use CAST(text AS XML) to obtain an XML typed column that can be manipulated at the server (use value, nodes and query xml methods on it).
SELECT ID,
CAST(Request AS XML) As RequestXml,
CAST(Request AS XML) As ResponsetXml,
MerchantId,
Amount
FROM <table>
If you only need the XML in the client, then you can just return the text and then use whatever is your client side XML technology of choice (XmlDocument, XPathDocument, Linq 2 xml) they all allow you to cosntruct a fragment from a string.

You could cast the data on the fly, e.g.:
CAST(Request AS XML)

SELECT request.VALUE(/xpath/query)
FROM
(
SELECT
CAST(Request as XML) request,
CAST(Response as XML) response
FROM purchaseTbl
WHERE ...
) tbl
WHERE ...

You can always CAST( [Request] AS XML ) and then use all of the regular XML functions to extract data from it.

If you are using SQL server (2005 onwards), one possibility could be to write a user defined function in a .Net language that parses out the XML from a field and returns that data as part of teh query

Related

How to get a exact number from a String in sql server?

How to get Order Numbers(189,190) from following TrigParams field in SQL server 2014 Database.
TrigParams
{"OWLSObjKey":{"key":"OWLSObjKey","value":"189","type":null},"OWLSObjType":{"key":"OWLSObjType","value":"17","type":null},"ObjKey":{"key":"ObjKey","value":"189","type":null},"ObjType":{"key":"ObjType","value":"17","type":null}}
{"OWLSObjKey":{"key":"OWLSObjKey","value":"190","type":null},"OWLSObjType":{"key":"OWLSObjType","value":"17","type":null},"ObjKey":{"key":"ObjKey","value":"190","type":null},"ObjType":{"key":"ObjType","value":"17","type":null}}
You see from the other answers, that SQL-Server 2016+ with broad JSON support would be of great help. But without you are not lost. You can use string methods:
credits to Panagiotis Kanavos for the MCVE
declare #table table (trigparams nvarchar(2000))
insert into #table
values
('{"OWLSObjKey":{"key":"OWLSObjKey","value":"189","type":null},"OWLSObjType":{"key":"OWLSObjType","value":"17","type":null},"ObjKey":{"key":"ObjKey","value":"189","type":null},"ObjType":{"key":"ObjType","value":"17","type":null}}'),
('{"OWLSObjKey":{"key":"OWLSObjKey","value":"190","type":null},"OWLSObjType":{"key":"OWLSObjType","value":"17","type":null},"ObjKey":{"key":"ObjKey","value":"190","type":null},"ObjType":{"key":"ObjType","value":"17","type":null}}')
--This is the query
select LEFT(CutOff,CHARINDEX('"',CutOff)-1)
from #table t
CROSS APPLY(SELECT STUFF(t.trigparams,1,CHARINDEX('"value":"',t.trigparams)+8,'')) A(CutOff);
The idea in short:
Within the APPLY we will use STUFF() to write nothing over the first characters up to the number you are looking for (after the first occurance of "value":". This is returned as Column CutOff. We can now use LEFT() to pick the number only.
The question doesn't specify the server version, the table schema or whether the string represents a single value or the contents of two separate rows.
I'll just assume it's SQL Server 2016 which supports JSON and the text comes from two separate rows. I'll also assume the query only needs to return the data as single value.
In this case a simple call to JSON_VALUE('$.OWLSObjKey.value') will return the data. JSON_VALUE returns a single value from a well-formed JSON string :
declare #table table (trigparams nvarchar(2000))
insert into #table
values
('{"OWLSObjKey":{"key":"OWLSObjKey","value":"189","type":null},"OWLSObjType":{"key":"OWLSObjType","value":"17","type":null},"ObjKey":{"key":"ObjKey","value":"189","type":null},"ObjType":{"key":"ObjType","value":"17","type":null}}'),
('{"OWLSObjKey":{"key":"OWLSObjKey","value":"190","type":null},"OWLSObjType":{"key":"OWLSObjType","value":"17","type":null},"ObjKey":{"key":"ObjKey","value":"190","type":null},"ObjType":{"key":"ObjType","value":"17","type":null}}')
select JSON_VALUE(trigparams,'$.OWLSObjKey.value') As SomeKey
from #table
This returns :
SomeKey
189
190
Assuming you're using SQL Server 2016+, use OPENJSON:
SELECT O.OrderNumber
FROM (VALUES('{"OWLSObjKey":{"key":"OWLSObjKey","value":"189","type":null},"OWLSObjType":{"key":"OWLSObjType","value":"17","type":null},"ObjKey":{"key":"ObjKey","value":"189","type":null},"ObjType":{"key":"ObjType","value":"17","type":null}}'),
('{"OWLSObjKey":{"key":"OWLSObjKey","value":"190","type":null},"OWLSObjType":{"key":"OWLSObjType","value":"17","type":null},"ObjKey":{"key":"ObjKey","value":"190","type":null},"ObjType":{"key":"ObjType","value":"17","type":null}}')) V(TrigParams)
CROSS APPLY OPENJSON(V.TrigParams) WITH (OWLSObjKey nvarchar(MAX) AS JSON) OK
CROSS APPLY OPENJSON(OK.OWLSObjKey) WITH (OrderNumber int '$.value') O;
If not, then SQL Server is not your friend for this without using a CLR Function. If you can't use CLR, then I would suggest using a different tool to read the JSON value.

Retrieving multiple xml child node values

I have a column of type varchar(max) populated with xml nodes and values; as an example, the column data starts with <tag1> <tag2>value1</tag2><tag3>value2</tag3>... </tag1>. What I need to get out of this string is "value1 value2 value3... valueN" within one cell for every row in the table using static SQL or a stored procedure. The node tree isn't always the same, sometimes the path is <tagX><tagY>valueY</tagY>...</tagX>.
All of my experience with shredding xml is only used to get one specific value, property, or tag, not all values while retaining the column and row count. Currently I query then loop through the result set on my product's end and shred everything, but that's no longer an option due to recent changes.
It's possible to change the column to be of type xml, but if possible I'd like to avoid having to do so.
Cast the column to XML (or change it in the table to XML) and shred the xml on //* to get all nodes in a table. Then you can use for xml path to concat the values back together.
select (
select ' '+X.N.value('text()[1]', 'varchar(max)')
from (select cast(T.XMLCol as xml)) as T1(XMLCol)
cross apply T1.XMLCol.nodes('//*') as X(N)
for xml path(''), type
).value('substring(text()[1], 2)', 'varchar(max)')
from T
SQL Fiddle

SQL query to check for inclusion of any element from an array

I have a database column containing a string that might look something like this u/1u/3u/19/g1/g4 for a particular row.
Is there a performant way to get all rows that have at least one of the following elements ['u/3', 'g4'] in that column?
I know I can use AND clauses, but the number of elements to verify against varies and could become large..
I am using RoR/ActiveRecord in my project.
in sql server, you can use XML to convert your list of search params into a record set, then cross join that with the base table, and do charIndex() to see if the column contains the substring.
Since i don't know your table or column names, i used a table (persons) that i already had data in, which has a column 'phone_home'. To search for any phone number that contains '202' or '785' i would use this query:
select person_id,phone_home,Split.data.value('.', 'VARCHAR(10)')
from (select *, cast('<n>202</n><n>785</n>' as XML) as myXML
from persons) as data cross apply myXML.nodes('/n') as Split(data)
where charindex(Split.data.value('.', 'VARCHAR(10)'),data.phone_Home) > 0
you will get duplicate records if it matches more than one value, so throw a distinct in there and remove the Split from the select statement if that is not desired.
Using xml in sql is voodoo magic to me...i got the idea from this post http://www.sqljason.com/2010/05/converting-single-comma-separated-row.html
no idea what performance is like...but at least there aren't any cursors or dynamic sql.
EDIT: Casting the XML is pretty slow, so i made it a variable so it only gets cast once.
declare #xml XML
set #xml = cast('<n>202</n><n>785</n>' as XML)
select person_id,phone_home,Split.persons.value('.', 'VARCHAR(10)')
from persons cross apply #xml.nodes('/n') as Split(persons)
where charindex(Split.persons.value('.', 'VARCHAR(10)'),phone_Home) > 0

Querying by a value in XML in SQL Server 2005

Let's say I have a column in a table where the datatype is XML. I have a specific value I want query for in an xml tag that is unique(no repeating) in the XML. How do I do this?
Something like:
select * from MyTable
where XMLColumn.TagImLookingAt.Value = #QueryValue
Use:
WHERE xmlcolumn.value('(/path/to/tag)[1]', 'int') = #QueryValue
Change the data type to whatever is appropriate.
For more info, see the documentation - specifically the methods available when dealing with the XML data type...

XML XQUERY Problem with NTEXT data type

I want to use XQuery on a column of data type NTEXT (I have no choice!). I have tried converting the column to XML using CONVERT but it gives the error:
Incorrect syntax near the keyword 'CONVERT'.
Here's the query
SELECT
y.item.value('#UserID', 'varchar(50)') AS UnitID,
y.item.value('#ListingID', 'varchar(100)') AS #ListingID
FROM
dbo.KB_XMod_Modules
CROSS APPLY
CONVERT(xml, instancedata).nodes('//instance') AS y(item)
(instancedata is my column)
Can anyone think of a work around for this ?
Thanks
It seems that neither CONVERT nor CAST(... AS XML) work.
The XQuery stuff really only works on XML columns - sorry.
However: maybe there's an escape route :-)
You could do one of two things:
add a persisted computed column of type XML to your table and party on that new column
or - if you can't change the table structure -
create a view over that table and include a XML-casted column
So for solution #1 you'd do:
ALTER TABLE KB_XMod_Modules
ADD XmlCol AS CAST(instancedata AS XML) PERSISTED
and for solution #2, you'd use:
CREATE VIEW KBwithXML AS
SELECT instancedata, CAST(instancedata AS XML) 'XmlCol'
FROM KB_XMod_Modules
In both cases, you now have a XmlCol of type XML available, which you can query with XQuery to your heart's delight!