I have a table that contains some meta data in an XML field.
For example
<Meta>
<From>tst#test.com</From>
<To>
<Address>testing#123.com</Address>
<Address>2#2.com</Address>
</To>
<Subject>ESubject Goes Here</Subject>
</Meta>
I want to then be able to query this field to return the following results
From To Subject
tst#test.com testing#123.com Subject Goes Here
tst#test.com 2#2.com Subject Goes Here
I've written the following query
SELECT
MetaData.query('data(/Meta/From)') AS [From],
MetaData.query('data(/Meta/To/Address)') AS [To],
MetaData.query('data(/Meta/Subject)') AS [Subject]
FROM
Documents
However this only returns one record for that XML field. It combines both the 2 addresses into one result. Is it possible for split these on to separate records?
The result I'm getting is
From To Subject
tst#test.com testing#123.com 2#2.com Subject Goes Here
Thanks
Gav
You need to return the XML and then parse it using something like the following code:
StringReader stream = new StringReader(stringFromSQL);
XmlReader reader = XmlReader.Create(stream);
while (reader.Read())
{
// Do stuff
}
Where stringFromSQL is the whole string as read from your table.
Related
I have a json column with an XML field I am trying to parse
{
"guid": "ba410633-d191-deab-fe63-23f732c517aa",
"id": 1847510,
"request":<?xml version='1.0' encoding='UTF-8'?><ConnectRequest xmlns='http://www.acme.com/NetConnect' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://www.acme.com/API'><EAI>QWE</EAI><DBHost>TEST</DBHost><ReferenceId>aa</ReferenceId><Request xmlns='http://www.acme.com' version='1.0'><Products><PreciseIDServer><XMLVersion>5.0</XMLVersion><Subscriber><Preamble>ABC</Preamble><OpInitials>BCD</OpInitials><SubCode>2436170</SubCode></Subscriber></PreciseIDServer></Products></Request></ConnectRequest>"
}
XML field is like this
<?xml version='1.0' encoding='UTF-8'?>
<ConnectRequest xmlns='http://www.acme.com/NetConnect' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://www.acme.com/API'>
<EAI>QWE</EAI>
<DBHost>TEST</DBHost>
<ReferenceId>aa</ReferenceId>
<Request xmlns='http://www.acme.com' version='1.0'>
<Products>
<PreciseIDServer>
<XMLVersion>5.0</XMLVersion>
<Subscriber>
<Preamble>ABC</Preamble>
<OpInitials>BCD</OpInitials>
<SubCode>2436170</SubCode>
</Subscriber>
</PreciseIDServer>
</Products>
</Request>
</ConnectRequest>
I have tried the following but no luck, it returns null all the time
select xmlget(request, 'DBHost'):"$" as DBHost
from (select json_field:request::variant request from table)
Is it a datatype issue? I was able to parse another column from a table which has a variant column with xml data using the same xmlget.
with
XML(DBHOST, PRODUCTS, PRECISEIDSERVER, SUBCODE) as
(
select get(xmlget(parse_xml(json_field:request::string), 'DBHost'), '$')::string as DBHOST,
get(xmlget(parse_xml(json_field:request::string), 'Request'), '$')::string as PRODUCTS,
get(xmlget(parse_xml(PRODUCTS::string), 'PreciseIDServer'), '$')::string as PreciseIDServer,
get(xmlget(parse_json(PreciseIDServer)[1], 'SubCode'), '$')::int as SourceCode
from MY_TABLE
)
select DBHOST, SUBCODE from XML;
XMLGET would work that way on a variant column that contains only XML. In this case, the XML is not the variant. The JSON is the variant and the XML is one of the properties. It needs to be treated as a string property, and then manipulated as XML. To do that you need to convert it from a JSON property using ::string. You then need to use PARSE_XML to convert the string into a variant. Finally, you can use xmlget on the variant resulting from PARSE_XML and use GET with the final parameter of $ to indicate that you just want the value, not the begin and end tags too. The final ::string converts that final result into a string so that it doesn't get wrapped in double quotes.
One note - there's a missing close tag in the sample xml that fails when using the PARSE_XML function. I added the close tag for testing. The XML must be valid in order to convert from a string to an XML variant.
Edit: Updated to a CTE to add parsing for SubCode. To get at an XML node that's nested a few layers deep, a good approach is taking it step by step in columns with a CTE to select only the ones you want.
Context: I'm scraping some XML form descriptions from a Web Services table in hopes of using that name to identify what the user has inputted as response. Since this description changes for each step (row) of the process and each product I want something that can evaluate dynamically.
What I tried: The following was quite useful but it returns a dynamic attribute query result in it's own field ans using a coalesce to reduce the results as one field would lead to it's own complications: Get values from XML tags with dynamically specified data fields
Current Attempt:
I'm using the following code to generate the attribute name that I will use in the next step to query the attribute's value:
case when left([Return], 5) = '<?xml'
then lower(cast([Return] as xml).value('(/response/form/*/#name)[1]','varchar(30)'))
else ''
end as [FormRequest]
And as part of step 2 I have used the STUFF function to try and make the row-level query possible
case when len(FormRequest)>0
then stuff( ',' + 'cast([tmpFormResponse] as xml).value(''(/wrapper/#' + [FormRequest] + ')[1]'',''varchar(max)'')', 1, 1, '')
else ''
end as [FormResponse]
Instead of seeing 1 returned as my FormReponse feild value for the submit attribute (please see in yellow below) it's returning the query text -- cast([tmpFormResponse] as xml).value('(/wrapper/#submit)1','varchar(max)') -- instead (that which should be queried).
How should I action the value method so that I can dynamically strip out the response per row of XML data in tmpFormResponse based on the field value in the FormRequest field?
Thanx
You can check this out:
DECLARE #xml XML=
N'<root>
<SomeAttributes a="a" b="b" c="c"/>
<SomeAttributes a="aa" b="bb" c="cc"/>
</root>';
DECLARE #localName NVARCHAR(100)='b';
SELECT sa.value(N'(./#*[local-name()=sql:variable("#localName")])[1]','nvarchar(max)')
FROM #xml.nodes(N'/root/SomeAttributes') AS A(sa)
Ended up hacking up a solution to the problem by using PATINDEX and CHARINDEX to look for the value in the [FormRequest] field in the he tmpFormResponse field.
I have read dozens of posts and have tried numerous SQL queries to try and get this figured out. Sadly, I'm not a SQL expert (not even a novice) nor am I an XML expert. I understand basic queries from SQL, and understand XML tags, mostly.
I'm trying to query a database table, and have the data show a list of values from a column that contains XML. I'll give you an example of the data. I won't burden you with everything I have tried.
Here is an example of field inside of the column I need. So this is just one row, I would need to query the whole table to get all of the data I need.
When I select * from [table name] it returns hundreds of rows and when I double click in the column name of 'Document' on one row, I get the information I need.
It looks like this:
<code_set xmlns="">
<name>ExampleCodeTable</name>
<last_updated>2010-08-30T17:49:58.7919453Z</last_updated>
<code id="1" last_updated="2010-01-20T17:46:35.1658253-07:00"
start_date="1998-12-31T17:00:00-07:00"
end_date="9999-12-31T16:59:59.9999999-07:00">
<entry locale="en-US" name="T" description="Test1" />
</code>
<code id="2" last_updated="2010-01-20T17:46:35.1658253-07:00"
start_date="1998-12-31T17:00:00-07:00"
end_date="9999-12-31T16:59:59.9999999-07:00">
<entry locale="en-US" name="Z" description="Test2" />
</code>
<displayExpression>[Code] + ' - ' + [Description]</displayExpression>
<sortColumn>[Description]</sortColumn>
</code_set>
Ideally I would write it so it runs the query on the table and produces results like this:
Code Description
--------------------
(Data) (Data)
Any ideas? Is it even possible? The dozens of things I have tried that are always posted in stack, either return Nulls or fail.
Thanks for your help
Try something like this:
SELECT
CodeSetId = xc.value('#id', 'int'),
Description = xc.value('(entry/#description)[1]', 'varchar(50)')
FROM
dbo.YourTableNameHere
CROSS APPLY
YourXmlColumn.nodes('/code_set/code') AS XT(XC)
This basically uses the built-in XQuery to get an "in-memory" table (XT) with a single column (XC), each containing an XML fragment that represents each <code> node inside your <code_set> root node.
Once you have each of these XML fragments, you can use the .value() XQuery operator to "reach in" and grab some pieces of information from it, e.g. it's #id (attribute by the name of id), or the #description attribute on the contained <entry> subelement.
The following query will read the xml field in every row, then shred certain values into a tabular result set.
SELECT
-- get attribute [attribute name] from the parent node
parent.value('./#attribute name','varchar(max)') as ParentAttributeValue,
-- get the text value of the first child node
child.value('./text()', 'varchar(max)') as ChildNodeValueFromFirstChild,
-- get attribute attribute [attribute name] from the first child node
child.value('./#attribute name', 'varchar(max)') as ChildAttributeValueFromFirstChild
FROM
[table name]
CROSS APPLY
-- create a handle named parent that references that <parent node> in each row
[xml field name].nodes('//xpath to parent name') AS ParentName(parent)
CROSS APPLY
-- create a handle named child that references first <child node> in each row
parent.nodes('(xpath from parent/to child)[0]') AS FirstChildNode(child)
GO
Please provide the exact values you want to shred from the XML for a more precise answer.
I've got a table in a SQL Server 2008 database with an nvarchar(MAX) column containing XML data. The data represents search criteria. Here's what the XML looks like for search criteria with one top-level "OR" group containing one single criterion and a nested two-criterion "AND" group.
<?xml version="1.0" encoding="utf-16"?>
<SearchCriterionGroupArgs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SingleCriteria>
<SearchCriterionSingleArgs>
<Operator>Equals</Operator>
<Value>test</Value>
<FieldIDs>
<int>1026</int>
<int>478</int>
</FieldIDs>
<EntityID>92</EntityID>
</SearchCriterionSingleArgs>
</SingleCriteria>
<GroupCriteria>
<SearchCriterionGroupArgs>
<SingleCriteria>
<SearchCriterionSingleArgs>
<Operator>GreaterThan</Operator>
<Value>2010-01-23</Value>
<FieldIDs>
<int>1017</int>
</FieldIDs>
<EntityID>92</EntityID>
</SearchCriterionSingleArgs>
<SearchCriterionSingleArgs>
<Operator>LessThan</Operator>
<Value>2013-01-23</Value>
<FieldIDs>
<int>1018</int>
</FieldIDs>
<EntityID>92</EntityID>
</SearchCriterionSingleArgs>
</SingleCriteria>
<GroupCriteria />
<EntityID>92</EntityID>
<LogicalOperator>AND</LogicalOperator>
</SearchCriterionGroupArgs>
</GroupCriteria>
<EntityID>92</EntityID>
<LogicalOperator>OR</LogicalOperator>
</SearchCriterionGroupArgs>
Given a an input set of FieldID values, I need to search the table to find if there are any records whose search criteria refer to one of those values (these are represented in the "int" nodes under the "FieldIDs" nodes.)
By running this query:
select CAST(OptionalConditions as xml).query('//FieldIDs')
from tblMyTable
I get the results:
<FieldIDs>
<int>1026</int>
<int>478</int>
</FieldIDs>
<FieldIDs>
<int>1017</int>
</FieldIDs>
<FieldIDs>
<int>1018</int>
</FieldIDs>
(currently there's only one record in the table with xml data in it.)
But I'm just getting started with this stuff and I don't know what the notation would be to check those lists for the existence of any of an arbitrary set of FieldIDs. I don't need to retrieve any particular nodes, just true or false for whether the input field IDs are referenced anywhere in the search.
Thanks for your help!
Edit: using Ranon's solution, I got it working using a query like this:
SELECT *
FROM myTable
WHERE CAST(OptionalConditions as xml).exist('//FieldIDs/int[.=(1019,111,1018)]') = 1
Fetch all FieldIDs and compare them with the set id IDs to check against. XQuery's =-operator compares in a set-based semantics, so if one of the IDs on the left side equal on one the right, this expression will evaluate to true.
//FieldIDs/int = (42, 478)
As "478" is a FieldID, this query will return true. "42" is one not available.
I'm not sure about whether you will be able to cast the result to some sql-server-boolean-type as I haven't got one running, but you will easily be able to try out yourself.
If you're also interested in the nodes contained, you could use this query:
//FieldIDs/int[. = (42,478)]
I have the XML response from the Google Geocoding API stored in a SQL Server XML column. Can someone help me with a query that will return all rows where the XML contains > 1 result?
e.g.
<GeocodeResponse>
<status>OK</status>
<result></result> <!-- more than one result is present -->
<result></result>
<result></result>
</GeocodeResponse>
So something like this gives me the first result:
SELECT XmlResponse.query('/GeocodeResponse/result') FROM Locations
But I'm not sure where to go from here...
You can use the exist() method of the XML data type to check if there exist a second <result> node.
select *
from Locations
where XmlResponse.exist('GeocodeResponse/result[2]') = 1
Test the query here. https://data.stackexchange.com/stackoverflow/q/101340/xmlcolumn-exist