Extract field from XML Data in SQL - sql

I have an issue as I never done that before.
I have an SQL table with the following :
ID int;
xml_record xml;
The xml record is looking like that :
<root xml:space="preserve" id="XXX">
<c1>Data1</c1>
<c2>Data2</c2>
<c3>Data3</c3>
...
<cn>DataN</cn>
</root>
However I tried to use the following query with no success (return null) :
SELECT xml_record.value('c1[1]','varchar(50)') as value_c1
FROM myTable
The problem might come from the "space" but not sure.

You just need to fix the expression:
SELECT xml_record.value('(/root/c1)[1]','varchar(50)') AS value_c1
FROM ...

SELECT
xml_record.value
( '(/root/c1/text())[1])',
'varchar(50)') as value_c1
FROM myTable
else remove the first xml line

Related

SQL query for XML data

I have a SQL Server database table with a column called XML that contains XML data which is structured like this:
<Item xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://test/data">
<Roots>
<Root>
<Name>Field Name</Name>
<Value>Field Value</Value>
</Root>
<Root>
<Name>Field Name</Name>
<Value>Field Value</Value>
</Root>
</Roots>
I want to use T-SQL to get the Value where Name = Total. I have tried the following but it isn't returning any data:
SELECT [XML]
FROM [BusinessAccount]
WHERE [XML].value('(/Root/Name)[13]', 'VARCHAR(MAX)') LIKE '%Total%'
If anyone could tell me where I've gone wrong?
You are missing the required WITH XMLNAMESPACES for your XML and the path is incorrect.
If you want to bring back rows where the 13th element consists of the text Total you can use the below.
WITH XMLNAMESPACES (DEFAULT 'http://test/data')
SELECT [XML]
FROM [BusinessAccount]
WHERE 1 = [XML].exist('(/Item/Roots/Root/Name)[13][text() = "Total"]')
Otherwise you can add the WITH XMLNAMESPACES to your original query and fix the path there too.
You need to specify namespaces. You can then match <Name> and <Value> pairs and extract the contents of <Value> like so:
SELECT NameNode.value('declare namespace x="http://test/data"; (../x:Value)[1]', 'varchar(100)')
FROM [BusinessAccount]
CROSS APPLY [XML].nodes('declare namespace x="http://test/data"; //x:Root/x:Name') AS n(NameNode)
WHERE NameNode.value('.', 'varchar(100)') = 'Total'
Demo on db<>fiddle

Count non-empty nodes in XML in SQL Server

I need to count all b nodes which are not empty (so result should be 2).
<a>
<b>1</b>
<b/>
<b>g</b>
</a>
I'm using code below but this returns count off all nodes (empty included).
select top 1 rc.XmlContent.value('count(//a/b)', 'int') from Table rc
If you use //a/b/text() rather than just //a/b, then you get a count of 2
DECLARE #x XML= '<a><b>1</b><b/><b>g</b></a>';
SELECT #x.value('count(//a/b/text())', 'int');
Sorry, this is not the answer! I misread this completely and thought your are looking for the empty nodes. There is an appropriate answer given by GarethD already (same idea, just the other way round).
I don't delete it, because it might help others...
The empty element <b/> (same as <b></b>) is existing but has no text().
DECLARE #xml XML =
N'<a>
<b>1</b>
<b/>
<b></b>
<b>g</b>
</a>';
select #xml.value('count(/a/b[empty(text())])', 'int')
This returns 2, because there is <b/> and <b></b>.
Just for completeness, you might negate the predicate, which is your needed result actually:
select #xml.value('count(/a/b[not(empty(text()))])', 'int')
Use this XPath expression
count(/a/b[normalize-space(text())=''])
Incorporated in your code it would look like this:
select top 1 rc.XmlContent.value('count(/a/b[normalize-space(text())=""])', 'int') from Table rc

SQL query to get most recent date from XML document

I am doing a SQL query against a column with an XML document located.
The XML document looks like the following.
<root>
<date>2016-10-12</date>
<date>2016-12-01</date>
<date>2016-11-13</date>
</root>
As you can see the dates are out of order.
I am looking for a SQL query that will get the most recent date from the XML document (in this case: 2016-12-01).
One way is to read all data and find the maximum externally (external ORDER BY with TOP 1, like in Prdp's answer, or MAX(), eventually with GROUP BY).
Another way is a FLWOR-XQuery:
DECLARE #xml XML=
'<root>
<date>2016-10-12</date>
<date>2016-12-01</date>
<date>2016-11-13</date>
</root>';
SELECT #xml.value('max(for $d in /root/date return xs:date($d))','date')
This means:
Take each value in /root/date, return it as date and find the highest!
Both approaches will need to read the whole list, but it should be a bit faster only to look for the maximum value, rather than return a full list and do some external sorting, picking again...
Try this
DECLARE #xml XML
SET #xml = '<root>
<date>2016-10-12</date>
<date>2016-12-01</date>
<date>2016-11-13</date>
</root>'
SELECT Top 1 x.col.value('.', 'date') AS dates
FROM #xml.nodes('/root/date') x(col)
ORDER BY dates DESC

Retrieve all XML elements with the same prefix in SQL Server

I have an XML file in a format similar to:
<XML>
<Field1>100</Field1>
<Field2>200</Field2>
<Field3>300</Field3>
<Test>400</Test>
</XML>
I need to write a query that will get all of the element values that start with Field. So given the XML above the result should be
FieldVal
--------
100
200
300
I've tried the following but it does not work:
Select
xc.value('text()', 'int')
From
#XMLData.nodes('/XML/[starts-with(name(), ''Field'')]') As xt(xc)
NOTE: I am well aware that this task could be easily done if I reformatted my XML but unfortunately I have no control over the format of the XML.
One way is
declare #XMLData xml ='<XML>
<Field1>100</Field1>
<Field2>200</Field2>
<Field3>300</Field3>
<Test>400</Test>
</XML>'
Select
xc.value('.', 'int')
From #XMLData.nodes('/XML/*') As xt(xc)
WHERE xc.value('local-name(.)', 'varchar(50)') LIKE 'Field%'
Prefix name with special character and check contains instead.
declare #x xml ='<XML>
<Field1>100</Field1>
<Field2>200</Field2>
<Field3>300</Field3>
<Test>400</Test>
</XML>';
select t.n.value('.','varchar(100)')
from #x.nodes ('XML/*[contains(concat("$",local-name()),"$Field")]') t(n);
I think it's this what you are looking for:
DECLARE #xml XML=
'<XML>
<Field1>100</Field1>
<Field2>200</Field2>
<Field3>300</Field3>
<Test>400</Test>
</XML>';
SELECT Fld.value('.','int') AS FieldOnly
FROM #xml.nodes('/XML/*[substring(local-name(.),1,5)="Field"]') AS A(Fld)
Just because of the discussion in comments:
DECLARE #fldName VARCHAR(100)='Field';
SELECT Fld.value('.','int') AS FieldOnly
FROM #xml.nodes('/XML/*[substring(local-name(.),1,string-length(sql:variable("#fldName")))=sql:variable("#fldName")]') AS A(Fld)
Change the first line to "Test" (case sensitive!), and you'd get just the one row with 400...

Change XML attribute values in SQL Server XML column

I have a SQL Server database table like the following:-
Id (int) Info(xml)
------------------------
1 <xml....
2 <xml ...
3 <xml ...
The XML in each record in the Info field is something like:-
<CodesParameter xmlns="http://mynamespace.com/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Data Code="A1" HasMoreData="true">
.
.
</Data>
</CodesParameter>
I would like to be able to change the value of the <Data Code="..."> within the XML and have tried the following query:-
UPDATE MyTable
SET Info.modify('replace value of (Data[1]/#Code)with ("B1")')
WHERE Id = 2
Go
The message backs says that the query has executed successfully, but the value does not change.
I have tried declaring the namespace:-
UPDATE MyTable
SET Info.modify('declare namespace myns="http://mynamespace.com/";
replace value of (/myns:WebTableParameter[1]/myns:DataTable[1]/#Code)with ("B1")')
WHERE Id = 2
Go
But the same result - the message says the query has executed successfully, but nothing has changed.
Any help appreciated.
You have several problems:
you're totally ignoring the XML namespace on the XML document in question (in your original UPDATE)
you're using an incomplete XPath expression for your modify
Try this statement:
;WITH XMLNamespaces(DEFAULT 'http://mynamespace.com/')
UPDATE dbo.MyTable
SET Info.modify('replace value of (/CodesParameter/Data[1]/#Code)[1] with ("B1")')
WHERE Id = 1
Basically, you need to start at the root of the XML document - you've missed (or left out) the <CodesParameter> node entirely. Also: you need to select a single attribute - thus you need another [1] after your parenthesis.
With this statement, I was able to update the XML as desired.