SQL XML Query nodes.value - sql-server

Microsoft SQL Server XML query: how can I get the data in ExtendedFields/Field/Identifier, ExtendedFields/Field/Identifier, IdentifierCode and OwnerID?
I am expecting to get values ID_US, US1 and John Smith.
Thanks
DECLARE #myDoc XML
DECLARE #ProdID varchar(30)
SET #myDoc = '<?xml version="1.0" encoding="UTF-8"?><MyFeed header="header value">
<f:TheFeed xmlns:f="urn:Thefeed-xsd">
<f:ConventionalValue>-100.681356</f:ConventionalValue><f:ReceiveXPercent>1.0</f:ReceiveXPercent><f:ReceiveXMonth>3</f:ReceiveXMonth>
<f:Fields><f:Field calcrt="SW1" name="Identifier">ID_US</f:Field><f:Field calcrt="SW5" name="IdentifierCode">US1</f:Field><f:Field calcrt="SW10" name="OwnerID">John Smith</f:Field>
</f:Fields>
</f:TheFeed>
</MyFeed>';
WITH XMLNAMESPACES(DEFAULT 'urn:Thefeed-xsd')
SELECT
OgrRol.value('(ConventionalValue/text())[1]','nvarchar(50)') AS ConventionalValue,
OgrRol.value('(ReceiveXPercent/text())[1]','nvarchar(50)') AS ReceiveXPercent,
OgrRol.value('(ReceiveXMonth/text())[1]','nvarchar(50)') AS ReceiveXMonth,
OgrRol.value('(ExtendedFields/#Identifier)[1]','nvarchar(50)') AS Identifier,
OgrRol.value('(ExtendedFields/#IdentifierCode)[1]','nvarchar(50)') AS IdentifierCode,
OgrRol.value('(ExtendedFields/#OwnerID)[1]','nvarchar(50)') AS OwnerID
FROM
#myDoc.nodes('/*:MyFeed/TheFeed') A(ogrRol)

Please try the following.
It is better not to use wildcard namespaces.
SQL
DECLARE #myDoc XML =
'<?xml version="1.0" encoding="UTF-8"?>
<MyFeed header="header value">
<f:TheFeed xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.fpml.org/2009/FpML-4-7"
xmlns:f="urn:Thefeed-xsd">
<f:ConventionalValue>-100.681356</f:ConventionalValue>
<f:ReceiveXPercent>1.0</f:ReceiveXPercent>
<f:ReceiveXMonth>3</f:ReceiveXMonth>
<f:ExtendedFields>
<f:Field calcrt="SW1" name="Identifier">ID_US</f:Field>
<f:Field calcrt="SW5" name="IdentifierCode">US1</f:Field>
<f:Field calcrt="SW10" name="OwnerID">John Smith</f:Field>
</f:ExtendedFields>
</f:TheFeed>
</MyFeed>';
WITH XMLNAMESPACES('urn:Thefeed-xsd' AS f)
SELECT c.value('(f:ConventionalValue/text())[1]','nvarchar(50)') AS ConventionalValue
,c.value('(f:ReceiveXPercent/text())[1]','nvarchar(50)') AS ReceiveXPercent
,c.value('(f:ReceiveXMonth/text())[1]','INT') AS ReceiveXMonth
,p.value('(f:Field[#calcrt="SW1"]/text())[1]','nvarchar(50)') AS Identifier
,p.value('(f:Field[#calcrt="SW5"]/text())[1]','nvarchar(50)') AS IdentifierCode
,p.value('(f:Field[#calcrt="SW10"]/text())[1]','nvarchar(50)') AS OwnerID
FROM #myDoc.nodes('/MyFeed/f:TheFeed') t1(c)
CROSS APPLY t1.c.nodes('f:ExtendedFields') AS t2(p);
Output
+-------------------+-----------------+---------------+------------+----------------+------------+
| ConventionalValue | ReceiveXPercent | ReceiveXMonth | Identifier | IdentifierCode | OwnerID |
+-------------------+-----------------+---------------+------------+----------------+------------+
| -100.681356 | 1.0 | 3 | ID_US | US1 | John Smith |
+-------------------+-----------------+---------------+------------+----------------+------------+

Related

multiple nodes with same name as rows

This may look like a duplicate, but what I can find are getting multiple rows from nodes with elements inside, like
<products>
<product>
<image>url1</image>
</product>
<product>
<image>url1</image>
</product>
</products>
What I have is an XML-field in a table (with PLU as an integer)
<product>
<images>
<image>url1</image>
<image>url2</image>
<image>url3</image>
</images>
</product>
I want
image
-----
url1
url2
url3
I tried
select a.image.value('image','nvarchar(max)') as image
from products r
cross apply r.xml.nodes('/product/images') a(image) where PLU='8019'
but that gives
XQuery [products.xml.value()]: 'value()' requires a singleton (or empty sequence),
found operand of type 'xdt:untypedAtomic *'
As I want the value of each node, not of subnodes, I tried
select a.image.value('.','nvarchar(max)') ...
but that gave me only one row with url1url2url3 all urls concatenated.
select a.image.value('image[1]','nvarchar(max)')
gives only url1
PLease try the following solution.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT #tbl (xmldata) VALUES
(N'<product>
<images>
<image>url1</image>
<image>url2</image>
<image>url3</image>
</images>
</product>');
-- DDL and sample data population, end
SELECT ID
, c.value('text()[1]','nvarchar(max)') AS image_url
FROM #tbl
CROSS APPLY xmldata.nodes('/product/images/image') AS t(c);
Output
+----+-----------+
| ID | image_url |
+----+-----------+
| 1 | url1 |
| 1 | url2 |
| 1 | url3 |
+----+-----------+
A shorter solution than Yitzhak Khabhinsky is this by Martin Boje Carpentier elsewhere, but I'll award the points to Yitzhak
select a.image.value('.','nvarchar(max)') as image
from products r
cross apply r.xml.nodes('/product/images/*') a(image) where PLU='8019'

Use the result of an XQuery value() query in another XQuery value() query in SQL Server

I'm trying to query the SSRS .rdl files which are in xml format. Here is an example of the relevant parts of the xml:
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition">
<DataSources>
<DataSource Name="DataSource1">
<DataSourceReference>DataSourceReference1</DataSourceReference>
</DataSource>
<DataSource Name="DataSource2">
<DataSourceReference>DataSourceReference2</DataSourceReference>
</DataSource>
</DataSources>
<DataSets>
<DataSet Name="DataSet1">
<Query>
<DataSourceName>DataSource1</DataSourceName>
<CommandText>SELECT a from b</CommandText>
</Query>
</DataSet>
<DataSet Name="DataSet2">
<Query>
<DataSourceName>DataSource2</DataSourceName>
<CommandText>SELECT c from d</CommandText>
</Query>
</DataSet>
</DataSets>
...
And here is my SQL query so far:
IF OBJECT_ID('tempdb..#catalogtemp') IS NOT NULL DROP TABLE #catalogtemp
GO
SELECT Path, CONVERT(XML, CONVERT(VARBINARY(MAX), Content)) XmlColumn
INTO #catalogtemp
FROM Catalog WHERE Type=2;
;WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition' as rdl10)
SELECT Path as ReportPath,
T1.dataset.value('./#Name','nvarchar(max)') DatasetName,
T1.dataset.value('(.//rdl10:CommandText)[1]','nvarchar(max)') as DatasetQuery,
T1.dataset.value('(.//rdl10:DataSourceName)[1]','nvarchar(max)') as DataSourceName
FROM #catalogtemp
CROSS APPLY xmlColumn.nodes('//rdl10:DataSet') T1(dataset)
For the example xml above this would return:
ReportPath DatasetName DatasetQuery DataSourceName
/path/to/report DataSet1 SELECT a from b DataSource1
/path/to/report DataSet2 SELECT c from d DataSource2
What I would like to do is add another column that looks up the DataSourceReference value for the DataSource node whose Name attribute matches the DataSourceName value of the DataSet. So the result of the query would look like this:
ReportPath DatasetName DatasetQuery DataSourceName DataSourceReference
/path/to/report DataSet1 SELECT a from b DataSource1 DataSourceReference1
/path/to/report DataSet2 SELECT c from d DataSource2 DataSourceReference2
I know that the following XQuery will return the DataSourceReference value for the DataSource node whose Name attribute is 'DataSource1':
T1.dataset.value('(../..//rdl10:DataSource[#Name="DataSource1"]//rdl10:DataSourceReference)[1]','nvarchar(max)')
but how do I write the query to look up the correct DataSourceName each time?
IMHO since <DataSources> are not included inside <DataSets> tags and viceversa, you need a subquery:
;WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition' as rdl10)
SELECT ct.Path as ReportPath,
T1.dataset.value('./#Name','nvarchar(max)') DatasetName,
T1.dataset.value('(.//rdl10:CommandText)[1]','nvarchar(max)') as DatasetQuery,
T1.dataset.value('(.//rdl10:DataSourceName)[1]','nvarchar(max)') as DataSourceName
,t.DSR
FROM catalogtemp ct
CROSS APPLY xmlColumn.nodes('//rdl10:DataSet') T1(dataset)
JOIN (SELECT Path,
T2.dsrc.value('(./#Name)[1]', 'nvarchar(max)') as DSName,
T2.dsrc.value('(.//rdl10:DataSourceReference)[1]', 'nvarchar(max)') as DSR
FROM catalogtemp
CROSS APPLY xmlColumn.nodes('//rdl10:DataSource') T2(dsrc)) t
ON t.Path = ct.Path
AND t.DSName = T1.dataset.value('(.//rdl10:DataSourceName)[1]','nvarchar(max)')
WHERE ct.Path = 'my report'
GO
ReportPath | DatasetName | DatasetQuery | DataSourceName | DSR
:--------- | :---------- | :-------------- | :------------- | :-------------------
my report | DataSet1 | SELECT a from b | DataSource1 | DataSourceReference1
my report | DataSet2 | SELECT c from d | DataSource2 | DataSourceReference2
dbfiddle here

How to do xml parsing in store procedure

I have a table which contains xml format request.
For eg:
Api_id xmlRequest Sent_Time
1 ........ 07-04-2016 10:07:12:345
1 ........ 08-04-2016 10:03:12:345
2 ........ 09-04-2016 10:08:12:345
2 ........ 09-04-2016 10:09:12:345
For Api_id, we can have multiple request.
XML request schema is same, but has different values.
Xml request is as :
<?xml version="1.0"?>
<!DOCTYPE PARTS SYSTEM "parts.dtd">
<PARTS>
<TITLE>Computer Parts</TITLE>
<PART>
<ITEM>Motherboard</ITEM>
<MANUFACTURER>ASUS</MANUFACTURER>
<MODEL>P3B-F</MODEL>
<COST> 123.00</COST>
</PART>
</PARTS>
I need store procedure, so I can send API_id, and value(which i can search in xml request ) and get xml requests based on Item value.
CREATE PROCEDURE getxmlRequest(
#Api_Id INT
#value
,#xmlRequest VARCHAR(max) out)
AS
BEGIN
Set #xmlRequest = SELECT xmlRequest FROM Api_request
WHERE Api_id = #Api_id
/* here need to iterate over #xmlRequest */
Set #Xmlvalue = SELECT X.R.value ('.','nvarchar (150)')
FROM #xmlRequest.nodes(XPATH) X(R)
if(#XmlValue = #value)
/*Add to result so i can return
/*I want to return all #xmlRequest if we has value from xpath*/
END;
So my question If
Set #xmlRequest = SELECT xmlRequest FROM Api_request
WHERE Api_id = #Api_id
If we will get multiple result : does it possible to iterate? If yes how efficient i can ?
How to return multiple #xmlRequest as Api_id is same?
Does any one work on such kind of scenario? Please help me.
Try this query
SELECT *,CONVERT(XML,xmlRequest,2)
FROM Api_request
WHERE Api_id = #Api_id
AND CONVERT(XML,xmlRequest,2).value('(/PARTS/PART/ITEM)[1]','nvarchar(max)')
LIKE '%'+#value+'%'
It will return all the xmlRequest where contains your value
Your question is quite unclear, but please have look on this:
CREATE TABLE #testTbl(Api_id INT, xmlRequest VARCHAR(MAX), SentTime DATETIME);
INSERT INTO #testTbl VALUES
(1,'<?xml version="1.0"?>
<!DOCTYPE PARTS SYSTEM "parts.dtd">
<PARTS>
<TITLE>Computer Parts</TITLE>
<PART>
<ITEM>Motherboard</ITEM>
<MANUFACTURER>ASUS</MANUFACTURER>
<MODEL>P3B-F</MODEL>
<COST> 123.00</COST>
</PART>
</PARTS>',GETDATE())
,(1,'<?xml version="1.0"?>
<!DOCTYPE PARTS SYSTEM "parts.dtd">
<PARTS>
<TITLE>Computer Parts</TITLE>
<PART>
<ITEM>CPU</ITEM>
<MANUFACTURER>INTEL</MANUFACTURER>
<MODEL>CPUModelXY</MODEL>
<COST>345.00</COST>
</PART>
</PARTS>',GETDATE())
,(2,'<?xml version="1.0"?>
<!DOCTYPE PARTS SYSTEM "parts.dtd">
<PARTS>
<TITLE>Car Parts</TITLE>
<PART>
<ITEM>Wheel</ITEM>
<MANUFACTURER>Pirelli</MANUFACTURER>
<MODEL>WheelModelXY</MODEL>
<COST>100.00</COST>
</PART>
</PARTS>',GETDATE());
This will rerturn all rows where the Api_id=1
SELECT Api_id
,CONVERT(XML,xmlRequest,2) AS xmlRequest
,SentTime
FROM #testTbl
WHERE Api_id=1;
This will return table-like data. You can use "normal" SQL (WHERE, GROUP BY, ...) to continue
DECLARE #Api_Id INT=NULL;
WITH MyRequests AS
(
SELECT Api_id
,RealXML.value('(/PARTS/TITLE)[1]','varchar(max)') AS Title
,part.value('ITEM[1]','varchar(max)') AS Item
,part.value('MANUFACTURER[1]','varchar(max)') AS Manufacturer
,part.value('MODEL[1]','varchar(max)') AS Model
,part.value('COST[1]','decimal(12,4)') AS Cost
,SentTime
FROM #testTbl
CROSS APPLY(SELECT CONVERT(XML,xmlRequest,2) AS RealXML) AS ConvertedToXML
CROSS APPLY RealXML.nodes('/PARTS/PART') AS A(part)
WHERE #ApiId IS NULL OR Api_Id=#Api_Id
)
SELECT *
FROM MyRequests
--WHERE ...
--GROUP BY ...
--ORDER ...
;
The result
+---+----------------+-------------+---------+--------------+----------+-------------------------+
| 1 | Computer Parts | Motherboard | ASUS | P3B-F | 123.0000 | 2016-04-07 11:54:08.980 |
+---+----------------+-------------+---------+--------------+----------+-------------------------+
| 1 | Computer Parts | CPU | INTEL | CPUModelXY | 345.0000 | 2016-04-07 11:54:08.980 |
+---+----------------+-------------+---------+--------------+----------+-------------------------+
| 2 | Car Parts | Wheel | Pirelli | WheelModelXY | 100.0000 | 2016-04-07 11:54:08.980 |
+---+----------------+-------------+---------+--------------+----------+-------------------------+
Clean Up
GO
DROP TABLE #testTbl;

TSQL: Extract XML Nested Tags Into Columns

Using SQLServer2008R2
I currently have XML tags with data inside the XML tags (not between them), such as:
<zooid="1"><animals key="all" zebras="22" dogs="0" birds="4" /><animals key="all" workers="yes" vacation="occasion" /> ... *(more)*</zooid>
<zooid="2"><animals key="house" zebras="0" dogs="1" birds="2" /><animals key="house" workers="no" vacation="no" /> ... *(more)*</zoodid>
If I query the XML or use the value function against it, it returns blank values because it tries to read between tags - where no value exists. I need it to read inside of the tags, parse out the values before the equal sign as columns and the values between the quotations as values inside those columns (granted, I could create a function that could do this, but this would be quite meticulous, and I'm curious if something like this already exists). What it should look like this in columns:
Key | Zebras | Dogs | Birds | Key | Workers | Vacation | ... *(more)*
... and this in rows of data:
all | 22 | 0 | 4 | all | yes | occasion | ... *(more)*
house | 0 | 1 | 2 | house | no | no | ... *(more)*
So the final output (just using the two XML rows from the beginning for now), would look like the below data in table form:
Key | Zebras | Dogs | Birds | Key | Workers | Vacation | ... *(more)*
================================================================
all | 22 | 0 | 4 | all | yes | occasion | ... *(more)*
house | 0 | 1 | 2 | house | no | no | ... *(more)*
Other than querying against XML, using the .query tool and even trying the .node tool (using CROSS APPLY see this thread), I haven't been able to generate this.
Try this one -
DECLARE #YourXML NVARCHAR(MAX)
SELECT #YourXML = '
<zooid="1">
<animals key="all" zebras="22" dogs="0" birds="4" />
<animals key="all" workers="yes" vacation="occasion" />
</zooid>
<zooid="2">
<animals key="house" zebras="0" dogs="1" birds="2" />
<animals key="house" workers="no" vacation="no" />
</zoodid>'
DECLARE #XML XML
SELECT #XML =
REPLACE(
REPLACE(#YourXML, 'zooid=', 'zooid id=')
, '</zoodid>'
, '</zooid>')
SELECT
d.[Key]
, Dogs = MAX(d.Dogs)
, Zebras = MAX(d.Zebras)
, Birds = MAX(d.Birds)
, Workers = MAX(d.Workers)
, Vacation = MAX(d.Vacation)
FROM (
SELECT
[Key] = t.p.value('./#key', 'NVARCHAR(50)')
, Zebras = t.p.value('./#zebras', 'INT')
, Dogs = t.p.value('./#dogs', 'INT')
, Birds = t.p.value('./#birds', 'INT')
, Workers = t.p.value('./#workers', 'NVARCHAR(20)')
, Vacation = t.p.value('./#vacation', 'NVARCHAR(20)')
FROM #XML.nodes('/zooid/animals') t(p)
) d
GROUP BY d.[Key]
Your xml appears invalid. How are you able to specify an element like this: ? Generally xml structure is <(elementName) (Attribute)="(Value)"/>. Unless I am mistaken if you are casting text to xml the way it is it will fail. Saying that I can show a working example for proper xml in a self extracting example that will run in SQL Managment Studio as is.
declare #text1 varchar(max) = '<zooid="1"><animals="all" zebras="22" dogs="0" birds="4" /><animals="all" workers="yes" vacation="occasion" /></zooid>'
, #text2 varchar(max) = '<a zooid="1"><b animals="all" zebras="22" dogs="0" birds="4" /><b animals="all" workers="yes" vacation="occasion" /></a>'
, #xml xml
;
begin try
set #xml = cast(#text1 as xml)
end try
begin catch
set #xml = '<ElementName Attribute="BadData Elements are not named" />'
end catch
select #xml
begin try
set #xml = cast(#text2 as xml)
end try
begin catch
set #xml = '<ElementName Attribute="BadData" />'
end catch
select
#xml.value('(/a/b/#animals)[1]', 'varchar(20)') as AnimalsValue
, #xml.value('(/a/b/#zebras)[1]', 'int') as ZebrasValue
, #xml.value('(/a/b/#dogs)[1]', 'int') as DogsValue
, #xml.value('(/a/b/#birds)[1]', 'int') as BirdsValue
, #xml.value('(/a/b/#workers)[1]', 'varchar(16)') as Workers
, #xml.value('(/a/b/#vacation)[1]', 'varchar(16)') as Vacation
The '.value' method is a syntax for querying xml in SQL. I am basically finding the elements(I did generics of a that contained b). Then once at the level I want '#animals' stands for 'attribute of name animals'. The [1] is a position since I can only return one thing at a time, so I chose the first position. Then it needs to a datatype to return. Text is varchar and numbers are ints.
XML query methods: http://msdn.microsoft.com/en-us/library/ms190798.aspx

Query XML field with T-SQL

How can I query multiple nodes in XML data with T-SQL and have the result output to a single comma separated string?
For example, I'd like to get a list of all the destination names in the following XML to look like "Germany, France, UK, Italy, Spain, Portugal"
<Holidays>
<Summer>
<Regions>
<Destinations>
<Destination Name="Germany" />
<Destination Name="France" />
<Destination Name="UK" />
<Destination Name="Italy" />
<Destination Name="Spain" />
<Destination Name="Portugal" />
</Destinations>
<Regions>
</Summer>
</Holidays>
I was trying something like:
Countries = [xmlstring].value('/Holidays/Summer/Regions/Destinations/#Name', 'varchar')
First, to get a list of records from a source XML table, you need to use the .nodes function (DEMO):
select Destination.value('data(#Name)', 'varchar(50)') as name
from [xmlstring].nodes('/Holidays/Summer/Regions/Destinations/Destination')
D(Destination)
Sample output:
| NAME |
-------------
| Germany |
| France |
| UK |
| Italy |
| Spain |
| Portugal |
From here, you want to concatenate the destination values into a comma-separated list. Unfortunately, this is not directly supported by T-SQL, so you'll have to use some sort of workaround. If you're working with a source table using multiple rows, the simplest method is the FOR XML PATH('') trick. In this query I use a source table called Data, and split out the XML into separate records, which I then CROSS APPLY with FOR XML PATH('') to generate comma-separated rows. Finally, the final , is stripped from the result to create the list (DEMO):
;with Destinations as (
select id, name
from Data
cross apply (
select Destination.value('data(#Name)', 'varchar(50)') as name
from [xmlstring].nodes('/Holidays/Summer/Regions/Destinations/Destination') D(Destination)
) Destinations(Name)
)
select id, substring(NameList, 1, len(namelist) - 1)
from Destinations as parent
cross apply (
select name + ','
from Destinations as child
where parent.id = child.id
for xml path ('')
) DestList(NameList)
group by id, NameList
Sample Output (Note that I've added another XML fragment to the test data to make a more complex example):
| ID | COLUMN_1 |
-----------------------------------------------
| 1 | Germany,France,UK,Italy,Spain,Portugal |
| 2 | USA,Australia,Brazil |

Resources