XML Help in TSQL - sql-server

We receive a full block of XML with various segments on it.
Need help in separating XML segments into separate XML chunks into local xml variables.
The local xml variable, holding the separated XML segments, will be passed on parameter to another stored procedure.
For eg:
Declare #Message xml
set #Message =
'<Message>
<Procedure>sp_testProc</Procedure>
<Customer>
<row>
<CustID>111</CustID>
<CustName>TestName2</CustName>
</row>
<row>
<CustID>222</CustID>
<CustName>TestName2</CustName>
</row>
</Customer>
<Product>
<ProdCode>AA</ProdCode>
<ProdName>TestProdAA</ProdName>
</Product>
</Message>'
select #Message
Declare #Proc xml
Declare #Customer XML
Declare #Product xml
----Need query help to extract as below, from #Message.
set #Proc = '<Procedure>sp_testProc</Procedure>'
set #Customer =
'<Customer>
<row>
<CustID>111</CustID>
<CustName>TestName2</CustName>
</row>
<row>
<CustID>222</CustID>
<CustName>TestName2</CustName>
</row>
</Customer>'
set #Product =
'<Product>
<ProdCode>AA</ProdCode>
<ProdName>TestProdAA</ProdName>
</Product>'

Your friend is called .query()
With these lines you'll get the portions separated:
Declare #Message xml
set #Message =
'<Message>
<Procedure>sp_testProc</Procedure>
<Customer>
<row>
<CustID>111</CustID>
<CustName>TestName2</CustName>
</row>
<row>
<CustID>222</CustID>
<CustName>TestName2</CustName>
</row>
</Customer>
<Product>
<ProdCode>AA</ProdCode>
<ProdName>TestProdAA</ProdName>
</Product>
</Message>';
SELECT #Message.query('/Message/Procedure') AS TheProc
,#Message.query('/Message/Customer') AS TheCust
,#Message.query('/Message/Product') AS TheProd
UPDATE
And this is the code to fill three variables at once
Declare #Proc xml
Declare #Customer XML
Declare #Product xml
SELECT #Proc=#Message.query('/Message/Procedure')
,#Customer=#Message.query('/Message/Customer')
,#Product=#Message.query('/Message/Product');
SELECT #Proc;
SELECT #Customer;
SELECT #Product

Related

Extract information from Datastore using OPEN XML

I have the following sample XML held in a database field:
<datastore multipleDataSeparator=",">
<group name="BasicDetails">
<field name="CalculationType">REFUND</field>
</group>
</datastore>
I am trying to extract the value held for CalculationType node using the following SQL script:
DECLARE #xml xml
DECLARE #idoc int
declare #lstrCalculationType varchar(50)
SET #xml = (
'<datastore multipleDataSeparator=",">
<group name="BasicDetails">
<field name="CalculationType">REFUND</field>
</group>
</datastore>'
)
select #xml
EXEC sp_xml_preparedocument #idoc OUTPUT, #xml --Preparing XML handle
select #idoc,#xml
SELECT
#lstrCalculationType = CalculationType
FROM OPENXML(#idoc, 'datastore') --Row Pattern
WITH (
CalculationType VARCHAR(50) 'BasicDetails/CalculationType'
)
select #lstrCalculationType
EXEC sp_xml_removedocument #idoc --Releasing memory
However, the output is null for #lstrCalculationType. Any guidance or input would be appreciated.
I'd use the built-in XQuery support to do this:
DECLARE #xml xml
SET #xml = ('<datastore multipleDataSeparator=",">
<group name="BasicDetails">
<field name="CalculationType">REFUND</field>
</group>
</datastore>')
SELECT
#xml.value('(/datastore/group/field[#name="CalculationType"]/text())[1]', 'varchar(25)')

Combined results of multiple stored procedures to a single XML

SQL Server 2014: looking for a solution as posted in [Combined results from multiple stored procedures to an XML][1] [1]: http://www.sqlservercentral.com/Forums/Topic1377879-21-1.aspx
I want the output to be like
<root>
<Products>
<...>
</Products>
<Colours>
<...>
</Colours>
<Sizes>
<...>
</Sizes>
</root>
And the code:
DECLARE #TempExportTable TABLE
(
Products XML,
Colours XML,
Sizes XML
)
INSERT INTO #TempExportTable VALUES
(
EXEC ('
EXEC [dbo].[spGetProductsDesc] #tId FOR XML AUTO, ELEMENTS;
EXEC [dbo].[spGetColoursDesc] #tId FOR XML AUTO, ELEMENTS;
EXEC [dbo].[spGetSizesDesc] #tId FOR XML AUTO, ELEMENTS;
');
)
SELECT
Products as '*',
Colours as '*',
Sizes as '*'
from #TempExportTable
FOR XML PATH('ExportList')
Is this achievable?
Try this (SP must output xml or nvarchar like xml data):
DECLARE #TempExportTable TABLE (Results xml);
INSERT INTO #TempExportTable
EXEC [dbo].[spGetProductsDesc] #tId;
--The output must be like:
--<Products>
-- <Product id="1" name="pr1" />
-- <Product id="2" name="pr2" />
-- ...
--</Products>
INSERT INTO #TempExportTable
EXEC [dbo].[spGetColoursDesc] #tId;
--The output must be like:
--<Colours>
-- <Colour id="1" name="red" />
-- <Colour id="2" name="white" />
-- ...
--</Colours>
INSERT INTO #TempExportTable
EXEC [dbo].[spGetSizesDesc] #tId;
--
--<Sizes>
-- <Size id="1" name="S" />
-- <Size id="2" name="M" />
-- ...
--</Sizes>
SELECT Results as '*'
FROM #TempExportTable
for xml PATH('') ,ROOT('root');
Results:
<root>
<Products>
<Product id="1" name="pr1" />
<Product id="2" name="pr2" />
<Product id="3" name="pr3" />
</Products>
<Colours>
<Colour id="1" name="red" />
<Colour id="2" name="white" />
<Colour id="3" name="green" />
</Colours>
<Sizes>
<Size id="1" name="S" />
<Size id="2" name="M" />
<Size id="3" name="L" />
</Sizes>
</root>
Your problem is, that these SPs deliver structurally not identical row sets probably. SPs are not the right way just to read data... Don't know what you are doing in your SPs, but this might be much easier with TVFs
Following an example how you could achieve what you want:
CREATE PROCEDURE dbo.Test1
AS
BEGIN
SELECT * FROM (VALUES(1,'Test1_1'),(2,'Test1_2')) AS tbl(t1a,t1b);
END
GO
CREATE PROCEDURE dbo.Test2
AS
BEGIN
SELECT * FROM (VALUES('a','Test1_A',100),('b','Test1_B',200)) AS tbl(t2a,t2b,t2c);
END
GO
CREATE PROCEDURE dbo.Test3
AS
BEGIN
SELECT * FROM (VALUES('a'),('b')) AS tbl(t3);
END
GO
--Here starts your solution
--First we need tables to fill the SPs results into
--The column's names don't have to be the same as delivered from the SP...
DECLARE #t1 TABLE(t1x INT,t1y VARCHAR(100));
INSERT INTO #t1 EXEC dbo.Test1;
DECLARE #t2 TABLE(t1x VARCHAR(100),t2y VARCHAR(100),t3z INT);
INSERT INTO #t2 EXEC dbo.Test2;
DECLARE #t3 TABLE(t3x VARCHAR(100));
INSERT INTO #t3 EXEC dbo.Test3;
SELECT (SELECT * FROM #t1 FOR XML PATH('T1_Row'),ROOT('T1'),TYPE)
,(SELECT * FROM #t2 FOR XML PATH('T2_Row'),ROOT('T2'),TYPE)
,(SELECT * FROM #t3 FOR XML PATH('T3_Row'),ROOT('T3'),TYPE)
FOR XML PATH(''),ROOT('root')
GO
DROP PROCEDURE dbo.Test1;
DROP PROCEDURE dbo.Test2;
DROP PROCEDURE dbo.Test3;
Exactly the same result, but much easier to call was this approach with scalar functions:
CREATE FUNCTION dbo.Test1()
RETURNS XML
AS
BEGIN
RETURN (SELECT * FROM (VALUES(1,'Test1_1'),(2,'Test1_2')) AS tbl(t1a,t1b) FOR XML PATH('T1_Row'),ROOT('T1'));
END
GO
CREATE FUNCTION dbo.Test2()
RETURNS XML
AS
BEGIN
RETURN (SELECT * FROM (VALUES('a','Test1_A',100),('b','Test1_B',200)) AS tbl(t2a,t2b,t2c) FOR XML PATH('T2_Row'),ROOT('T2'));
END
GO
CREATE FUNCTION dbo.Test3()
RETURNS XML
AS
BEGIN
RETURN (SELECT * FROM (VALUES('a'),('b')) AS tbl(t3) FOR XML PATH('T3_Row'),ROOT('T3'));
END
GO
SELECT (SELECT dbo.Test1())
,(SELECT dbo.Test2())
,(SELECT dbo.Test3())
FOR XML PATH(''),ROOT('root')
GO
DROP FUNCTION dbo.Test1;
DROP FUNCTION dbo.Test2;
DROP FUNCTION dbo.Test3;
The result in both cases:
<root>
<T1>
<T1_Row>
<t1x>1</t1x>
<t1y>Test1_1</t1y>
</T1_Row>
<T1_Row>
<t1x>2</t1x>
<t1y>Test1_2</t1y>
</T1_Row>
</T1>
<T2>
<T2_Row>
<t1x>a</t1x>
<t2y>Test1_A</t2y>
<t3z>100</t3z>
</T2_Row>
<T2_Row>
<t1x>b</t1x>
<t2y>Test1_B</t2y>
<t3z>200</t3z>
</T2_Row>
</T2>
<T3>
<T3_Row>
<t3x>a</t3x>
</T3_Row>
<T3_Row>
<t3x>b</t3x>
</T3_Row>
</T3>
</root>

USE Open XML to extract the CDATA

I have xml which has the data embedded in CDATA. I would like to extract the info in different fields. But not able to do it.
<Item_Response Format="text/xml">
<![CDATA[ <Item sequence="1" type="item" itemId="999999"
itemVersion="2012-04-07T13:43:27">
<response><bubbleinput answered="y" input_id="bubbleinput1">
<bubble id="bubble1"/>
</bubbleinput></response></Item> ]]>
</Item_Response>
You can extract CDATA value with XPath. Then use openxml on extracted value.
declare #xml xml = '<Item_Response Format="text/xml"><![CDATA[ <Item sequence="1" type="item" itemId="999999" itemVersion="2012-04-07T13:43:27"><response><bubbleinput answered="y" input_id="bubbleinput1"><bubble id="bubble1"/></bubbleinput></response></Item> ]]></Item_Response>'
-- openxml
declare
#idoc int,
#qxml xml = cast(#xml.value('(/Item_Response)[1]', 'nvarchar(max)') as xml)
exec sp_xml_preparedocument #idoc output, #qxml
select
*
from
openxml(#idoc, '/Item', 0) with (
sequence int '#sequence',
bubbleinput nvarchar(1) './response/bubbleinput/#answered'
) as XMLData
exec sp_xml_removedocument #idoc

SELECT node text values from xml document in TSQL OPENXML

I have a xml document I want to use to update values in a stored procedure. I can process the XML using OPENXML, but I'm confused about extracting the values I want. Each row in the xml is a product record and I want to create a variable for each property. Cell0 is the ID, Cell2 description etc
DECLARE #idoc int
DECLARE #doc varchar(1000)
SET #doc ='
<products>
<rows>
<row>
<cell>1</cell>
<cell>BALSAMO DERMOSCENT</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>18.00</cell>
<cell>18.00</cell>
<cell>8.00</cell>
<cell>427</cell>
<cell>No</cell>
</row>
<row>
<cell>2</cell>
<cell>BAYTRIL 150 MG 1 CPDO</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>3.50</cell>
<cell>3.50</cell>
<cell>8.00</cell>
<cell>57</cell>
<cell>No</cell>
</row>
</rows>
</products>'
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc
-- Execute a SELECT statement that uses the OPENXML rowset provider.
SELECT *
FROM OPENXML (#idoc, '/products/rows/row/cell',1)
with (Col1 varchar(29) 'text()')
Running the above query returns 1 record for each CELL in the xml. I want to be able to return 1 record per row with different columns for each cell, something like:-
Prod Description Qty
---------- -------------------- --------
1 BALSAMO DERMOSCENT 1.00
2 BAYTRIL 150 MG 1 CPDO 1.00
I'm using MSSQL 2008
I've come up with the following which does the job for me
DECLARE #idoc int
DECLARE #doc varchar(1000)
SET #doc ='
<products>
<rows>
<row>
<cell>1</cell>
<cell>BALSAMO DERMOSCENT</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>18.00</cell>
<cell>18.00</cell>
<cell>8.00</cell>
<cell>427</cell>
<cell>No</cell>
</row>
<row>
<cell>2</cell>
<cell>BAYTRIL 150 MG 1 CPDO</cell>
<cell>1.00</cell>
<cell>0.00</cell>
<cell>3.50</cell>
<cell>3.50</cell>
<cell>8.00</cell>
<cell>57</cell>
<cell>No</cell>
</row>
</rows>
</products>'
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc
-- Execute a SELECT statement that uses the OPENXML rowset provider.
SELECT *
FROM OPENXML (#idoc, '/products/rows/row',1)
with (pLineNo int 'cell[1]/text()',
pDesc varchar(50) 'cell[2]/text()',
pQty float 'cell[3]/text()',
pCost float 'cell[4]/text()',
pPvp float 'cell[5]/text()',
pTotal float 'cell[6]/text()',
pIva float 'cell[7]/text()',
pId int 'cell[8]/text()',
pnoFact varchar(5) 'cell[9]/text()')
Why use openxml on sql server 2008?
This is a better option (I used varchar(max) as the datatype, but enter whatever is applicable). Note you have to declare the variable as xml, not varchar.
SELECT
Row.Item.value('data(cell[1])', 'varchar(max)') As Prod,
Row.Item.value('data(cell[2])', 'varchar(max)') As Description,
Row.Item.value('data(cell[3])', 'varchar(max)') As Qty
FROM
#doc.nodes('//row') AS Row(Item)
Note: If you're doing this is a stored procedure you may have to include the following before the select statement:
SET ARITHABORT ON -- required for .nodes
If you must use openxml, at least clean it up when you're done:
exec sp_xml_removedocument #idoc

Inserting an attribute in multiple XML Nodes using XML.modify() in SQL 2005

I have an #XML document created from a single select statement.
<root>
<node>
<node1>
<targetNode>
</targetNode>
</node1>
<node1>
<targetNode>
</targetNode>
</node1>
<node1>
<targetNode>
</targetNode>
</node1>
</node>
<node>
......
</node>
</root>
I want to insert the xsi:nil as an attribute of 'targetNode' for this document.
#XML.modify( 'insert attribute xsi:nil {"true"} into (root/node/node1/targetNode) [1]')
The above will insert the attribute into the first occurance of the targetNode in the #XML document. The insert statement however will only work on a single node. Is there any way I can insert this attribute into all instances of targetNode in the #XML document.
I found a simple and elegant solution in DML operations on multiple nodes
http://blogs.msdn.com/b/denisruc/archive/2005/09/19/471562.aspx
The idea is to count how many nodes and modify them one by one:
DECLARE #iCount int
SET #iCount = #var.value('count(root/node/node1/targetNode)','int')
DECLARE #i int
SET #i = 1
WHILE (#i <= #iCount)
BEGIN
#xml.modify('insert attribute xsi:nil {"true"} into (root/node/node1/targetNode)[sql:variable("#i")][1]')
SET #i = #i + 1
END
That's not possible with the modify-function. It only works on a single node.
You can manipulate it as string, although that is definitely ugly and possibly wrong in some cases, depending on the actual structure of your XML.
Like this:
declare #xml as xml
set #xml = '<root>
<node>
<node1>
<targetNode>
</targetNode>
</node1>
<node1>
<targetNode>
</targetNode>
</node1>
<node1>
<targetNode>
</targetNode>
</node1>
</node>
</root>
'
set #xml = replace(cast(#xml as nvarchar(max)), '<targetNode/>', '<targetNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />')
select #xml
you can do this in the select, that you are using to create your xml, using the XSINILL parameter.
http://msdn.microsoft.com/en-us/library/ms178079.aspx
(here is a very rough example)
--create 2 tables and put some data in them
create table node
(
id int identity(1,1) primary key,
node int
)
GO
create table node1
(
id int identity(1,1) primary key,
nodeid int foreign key references node(id),
targetnode int
)
GO
insert into node
select 1
GO 5
insert into node1
select 1,2
union
select 2,null
union
select 3,2
union
select 4,null
--
--select statement to generate the xml
SELECT TOP(1)
(SELECT
( SELECT targetnode
FROM node1
WHERE nodeid = node.id
FOR XML AUTO,
ELEMENTS XSINIL,
TYPE
)
FROM node FOR XML AUTO,
ELEMENTS,
TYPE
)
FROM node FOR XML RAW('root'),
ELEMENTS

Resources