Retrieve all XML elements with the same prefix in SQL Server - sql

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...

Related

SQL Extract Value from XML

I have the following XML and I want to extract the FieldValue using SQL. How can I do this?
<Field Group="Annuitant">
<FieldName>Contract Number</FieldName>
<FieldValue>TR13116544</FieldValue>
</Field>
Thanks
DECLARE #xml XML=
'<Field Group="Annuitant">
<FieldName>Contract Number</FieldName>
<FieldValue>TR13116544</FieldValue>
</Field>';
--the lazy approach
SELECT #xml.value('(//FieldValue)[1]','varchar(50)')
--Better
SELECT #xml.value('(/Field/FieldValue)[1]','varchar(50)')
--This is, what you should do: Be as specific as possible...
SELECT #xml.value('(/Field/FieldValue/text())[1]','varchar(50)')
If there are - what I assume - are several <Field> elements and you need to pick the rigth one, you can do something like this:
DECLARE #name VARCHAR(100)='Contract Number';
SELECT #xml.value('(/Field[(FieldName/text())[1]=sql:variable("#name")]/FieldValue/text())[1]','varchar(50)')
Hint: Your question is not very clear, that needs a lot of guessing on my side. For your next question I ask you to be more specific.
Sample code:
DECLARE #MyXML XML
SET #MyXML ='<SampleXML>
<Fruits>
<Fruits1>Apple</Fruits1>
<Fruits2>Pineapple</Fruits2>
</Fruits>
</SampleXML>'
SELECT
a.b.value('Fruits[1]/Fruits1[1]','varchar(10)') AS Fruits1,
a.b.value('Fruits[1]/Fruits2[1]','varchar(10)') AS Fruits2
FROM #MyXML.nodes('SampleXML') a(b)
In oracle you can do the following:
WITH TABL (FIELD) AS (
select xmltype('<Field Group="Annuitant">
<FieldName>Contract Number</FieldName>
<FieldValue>TR13116544</FieldValue>
</Field>') from dual
)
SELECT
EXTRACTVALUE(FIELD,'/Field/FieldName') AS FIELDNAME,
EXTRACTVALUE(field,'/Field/FieldValue') AS FieldValue
FROM TABL ;

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)

sql server read xml without inner tags

i m reading an xml, but i have a problem when i want to read it without its inner(child) tags.
for example:
DECLARE #XMLToParse XML
SET #XMLToParse = '<Animals>
<LandAnimals>
<Animal>Baboon</Animal>
<Animal>Yak
<d>asd</d>
</Animal>
<Animal>Zebra</Animal>
</LandAnimals>
</Animals>'
SELECT xmlData.A.value('.', 'VARCHAR(100)') AS Animal
FROM #XMLToParse.nodes('Animals/LandAnimals/Animal') xmlData(A)
this query extracts on the second record 'Yak asd', but i need only 'Yak'.
how can i fix it?
thank you.!
Change your call on .value() to get the text():
SELECT xmlData.A.value('(./text())[1]', 'VARCHAR(100)') AS Animal
FROM #XMLToParse.nodes('Animals/LandAnimals/Animal') xmlData(A)
Specify text()
SELECT xmlData.A.value('./text()[1]', 'VARCHAR(100)') AS Animal
FROM #XMLToParse.nodes('Animals/LandAnimals/Animal') xmlData(A)

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

Getting the value of dc:creator using SQL XML

I am unsure how to get the value of dc:creator from an RSS-feed using SQL.
This is my xml/rss-feed:
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel>
<title>Foobar RSS</title>
<link>http://www.foobar.com/</link>
<description>RSS feed</description>
<language>en</language>
<ttl>15</ttl>
<item>
<title>This is my title</title>
<link>http://www.foobar.com/link/blabla</link>
<description>Bla..bla..bla..</description>
<dc:creator>John Doe</dc:creator>
<guid isPermaLink="false">00082EA751F1D905DE00E7CFA2417DA9</guid>
<pubDate>Wed, 26 Oct 2011 00:00:00 +0200</pubDate>
</item>
</channel>
</rss>
In my SQL I use something like this to get the values - e.g for pubDate I use something like this:
DECLARE #xml XML
SET #xml = cast('my rss feed here' AS xml)
SELECT
convert(datetime,substring(T.nref.value('pubDate[1]','nvarchar(100)'),6,20)) as pubdate,
FROM #xml.nodes('//item') AS T(nref)
This works fine, but when I am trying to get dc:creator value 'John Doe', the following just gives me an error:
SELECT
T.nref.value('dc:creator','nvarchar(100)') as creator
FROM #xml.nodes('//item') AS T(nref)
error:
XQuery [value()]: The name "dc" does not denote a namespace.
I need to be able to select multiple columns from the rss-feed. Can anybody provide a solution or direction to get the value of dc:creator?
I have another question - how would you construct the code if you are doing it in a sub select?
E.g.
INSERT INTO RSSResults (ID, pubDate)
SELECT #ID, tbl.pubDate FROM (
;WITH XMLNAMESPACES('http://purl.org/dc/elements/1.1/' AS dc)
SELECT
RSS.Item.value('(dc:creator)[1]', 'nvarchar(100)') as pubDate
FROM
#xml.nodes('/rss/channel/item') as RSS(Item)) AS tbl
The code breaks at ";WITH XMLNAMESPACES". Is it possible to include the namespace directly in the statement somehow?
Try something like this:
DECLARE #xml XML
SET #xml = cast('my rss feed here' AS xml)
;WITH XMLNAMESPACES('http://purl.org/dc/elements/1.1/' AS dc)
SELECT
#xml.value('(rss/channel/item/dc:creator)[1]', 'nvarchar(100)')
If you need to catch multiple items - try this:
DECLARE #xml XML
SET #xml = cast('my rss feed here' AS xml)
;WITH XMLNAMESPACES('http://purl.org/dc/elements/1.1/' AS dc)
SELECT
RSS.Item.value('(dc:creator)[1]', 'nvarchar(100)')
FROM
#xml.nodes('/rss/channel/item') as RSS(Item)