Catch value from within XML string - sql

Consider the following query:
SELECT [Details] FROM [History];
This is returning XML strings for [Details] like this:
<auditElement><field id="232131" type="4" name="Attachments" formatstring=""><oldValue>
<![CDATA[RB01B01_000001303; RB01B01_000001304]]></oldValue>
<newValue><![CDATA[ThisIsText]]></newValue></field></auditElement>
<auditElement><field id="232131" type="4" name="Attachments" formatstring=""><oldValue>
</oldValue>
<newValue><![CDATA[ThisIsText]]></newValue></field></auditElement>
So sometimes there is an <oldValue> and sometimes there isn't. Regardless, I want only return the CDATA within <oldValue> (or an empty row if there is no <oldValue>, so:
RB01B01_000001303; RB01B01_000001304
(NULL)
So I tried first
replace('<auditElement><field id="232131" type="4" name="Attachments" formatstring="">
<oldValue><![CDATA[', '', e.[Details]) as Details
but I get an error: String or binary data would be truncated.
How can I do this properly?

There are probably better ways, but try this query using the value method of the xml type:
select x.value('(auditElement/*/oldValue)[1]', 'nvarchar(max)') as result
from (select cast([Details] as xml) as x from [History]) a
I wasn't sure if your data already was typed as xml so I included a cast that might be unnecessary.
Sample SQL Fiddle

Related

Extract Value from XML having same tag name in SQL Server

I have XML variable defined below and its value.
I want to fetch the text defined between tag <TextNodeChild> in single query.
Kindly help.
Declare #XMLVariable =
'<?xml version="1.0"?>
<root>
<TextNodeParent>
<TextNodeChild>12345</TextNodeChild>
<TextNodeChild>67890</TextNodeChild>
<TextNodeChild>12389</TextNodeChild>
</TextNodeParent>
</root>'
I need output like this:
12345
67890
12389
You could use the XQuery (i.e. XML query) .nodes() method
SELECT
TextNodeParent = n.value('.[1]', 'NVARCHAR(max)')
FROM
#XMLVariable.nodes('root/TextNodeParent/*') as p(n)
EDIT : If you want to just the select the TextNodeChild node data then little change in xml path as follow
#XMLVariable.nodes('root/TextNodeParent/TextNodeChild') as p(n)
Result
TextNodeParent
12345
67890
12389
#YogeshSharma's solution works - here - because you have nothing but <TextNodeChild> elements under your <TextNodeParent> node.
However, if you had various node, and you wanted to extract only the <TextNodeChild> ones and get their values (and ignore all others), you'd have to use something like this instead:
SELECT
TextNodeParent = XC.value('.', 'INT')
FROM
#XMLVariable.nodes('root/TextNodeParent/TextNodeChild') as XT(XC)

Parse XML in T-SQL

I know that there are already numerous topics about querying xml in MS T-SQL, however with all the samples, I wasn't able to get my query to work properly.
I have the following XML:
<group>
<items>
<groupitem>
<key>23137</key>
</groupitem>
<groupitem>
<key>23139</key>
</groupitem>
<groupitem>
<key>23151</key>
</groupitem>
<groupitem>
<key>23153</key>
</groupitem>
</items>
</group>
I want to get all the 'key' items, so that I can insert them into a table (so 4 rows)
I started off putting my xml into variable #xml and running this query:
SELECT doc.value('(key/text())[1]', 'nvarchar(255)') AS 'key'
FROM #xml.nodes('/group/items/groupitem/*') AS ref(doc)
That gave me 4 empty rows, and if I remove the [1], it gives me this error: "XQuery [value()]: '*value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic '"
Then I tried this:
SELECT doc.value('(/group/items/groupitem/key)[1]', 'nvarchar(255)') AS 'key'
FROM #xml.nodes('/group/items/groupitem/*') AS ref(doc)
That actually gave me some data, but unfortunately, it's 4 times the same key 23137, probably caused by the [1] in the statement. Removing it however brings me back to the same error message as before.
I know how I should do it in Xpath (/group/items/groupitem/key), but can't get my head around how I should do it in T-Sql. Any ideas?
In the following query, context element of doc is already key element :
SELECT doc.value('(key/text())[1]', 'nvarchar(255)') AS 'key'
FROM #xml.nodes('/group/items/groupitem/*') AS ref(doc)
So you should not mention key again in the select clause. You can use . instead to reference current context element :
SELECT doc.value('.', 'nvarchar(255)') AS 'key'
FROM #xml.nodes('/group/items/groupitem/*') AS ref(doc)
Try this:
SELECT
doc.value('(key)[1]', 'int') AS 'key'
FROM
#xml.nodes('/group/items/groupitem') AS ref(doc)
In my case, this returns an output like:
key
-----
23137
23139
23151
23153
Is that what you're looking for?
The call to .nodes() basically gives you a "pseudo" table of XML fragments - one for each match of the XPath expression. So in your case, you get four rows of XML back, each representing the contents of the <groupitem> node. You reach into that, grab the value of the <key> element contained inside, cast it to an int - and presto, you're done!

Flattening xml data in sql

I'm trying to flatten XML data in a SQL query but I always seem to get nulls.
I tried the cross/outer apply method described here.
The column with XML data is called Data.
I'm guessing that the xml data with these links need to be somehow also added?
Could you please help to get a proper SQL query?
Query I tried:
SELECT
v.name
,pref.value('(LocalId/text())[1]', 'nvarchar(10)') as localid
FROM [database].[requests] v
outer apply v.Data.nodes('/DataForm') x(pref)
GO
example of xml data in that column:
<Dataform xmlns="http://somelongasslink.org/hasalsosomestuffhere" xmlns:i="http://somexlmschemalink/">
<DeleteDate xmlns="http://somelongasslink.org/hasalsosomestuffhere" i:nil="true" />
<LocalId xmlns="http://somelongasslink.org/hasalsosomestuffhere">5325325</LocalId>
...
You can use this code to get the result you're looking for:
;WITH XMLNAMESPACES(DEFAULT 'http://somelongasslink.org/hasalsosomestuffhere')
SELECT
rq.Name,
LocalID = TC.value('(LocalId)[1]', 'nvarchar(10)')
FROM
[database].[requests] rq
CROSS APPLY
rq.Data.nodes('/Dataform') AS TX(TC)
GO
There were two problems with your code:
you're not respecting / including the XML namespace that's defined on the XML document
<Dataform xmlns="http://somelongasslink.org/hasalsosomestuffhere"
*******************************************************
you didn't pay attention to the case-sensitivity of XML in your call to .nodes() - you need to use .nodes('/Dataform') (not /DataForm - the F is not capitalized in your XML)

Extracting values from XML field with unusual XML using SQL

Hoping someone can help -
The XML format was put together with a very simple syntax, but for some reason I'm struggling to parse it using a standard 'value' type query.
I'm experienced with SQL, but only have limited experience in XML, and after 2 hours of frustration and much Googling, I thought I'd ask for my own sanity!
The data is stored as a text string, so converting it to XML before parsing:
<!-- Data config file -->
<Config>
<!-- keys-->
<foo value="bar"/>
<foo1 value="bar1"/>
<big_foo value="bar/bar.com"/>
<other value="f00"/>
The query I'm using is:
SELECT
col.value('foo1[0]', 'nvarchar(max)') as VALUE
from
(
select
CAST((SELECT TOP 1 xml_text FROM dbo.xml_lookup)AS XML)
as Col)x
but this returns NULL rather than the expected "bar1".
Any idea where I'm going wrong?
proper XPath would be
col.value('(Config/foo1)[1]/#value', 'nvarchar(max)')
sql fiddle demo

Expecting a null returned from XML.value() in SQL, but getting 0

I'm attempting to read a specific value from an xml parameter passed into a stored procedure. A rough example of my code so far is below.
SET #SearchFilter =
'<?xml version="1.0"?>
<KeywordSearch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SelfEmployed xsi:nil="true" />
</KeywordSearch>'
SET #SelfEmployed = #SearchFilter.query('/KeywordSearch/SelfEmployed[1]').value('/', 'bit')
SELECT #SelfEmployed
The issue I'm having is that, although the above correctly returns when the element contains a true or false value, where the element contains no value, and thus I'm expecting null, it returns false. Is there a way to correctly obtain the datatype as a nullable bit value?
If you change your XQuery to something like this:
SELECT #SearchFilter.value('(/KeywordSearch/SelfEmployed/text())[1]', 'varchar(100)')
then you'll get a NULL value back. Since you're checking to see whether or not the <SelfEmployed> node has any textual value, you should be interpreting the .value() as a string type - I'm using varchar(100) here - adapt as needed.