Extract information from Datastore using OPEN XML - sql-server

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)')

Related

SQL Server and XML Utilization

I'm trying to utilize XML with SQL Server. All I'm trying to do is print out all three guests. When I run my code, it only shows the prints the first guest's information, and I need all three guest's information to be printed. What am I doing wrong?
SELECT Guest.GuestID, GuestFirst, GuestLast, CheckinDate, Nights
FROM GUEST
JOIN FOLIO
ON Guest.GuestID = Folio.GuestID
FOR XML RAW
Declare #idoc int
Declare #xmldoc nvarchar(4000)
Set #xmldoc = '
<ROOT>
<GUEST>
<GuestID>4431</GuestID>
<GuestFirst>Lacey</GuestFirst>
<GuestLast>Byington</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2016-08-02</CheckInDate>
<Nights>2</Nights>
</RESERVATIONDETAIL>
</GUEST>
<GUEST>
<GuestID>5563</GuestID>
<GuestFirst>Jonathan</GuestFirst>
<GuestLast>Langford</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2016-08-05</CheckInDate>
<Nights>2</Nights>
</RESERVATIONDETAIL>
</GUEST>
<GUEST>
<GuestID>6680</GuestID>
<GuestFirst>Tanner</GuestFirst>
<GuestLast>Olson</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2015-09-11</CheckInDate>
<Nights>3</Nights>
</RESERVATIONDETAIL>
</GUEST>
</ROOT>'
EXEC sp_xml_preparedocument #idoc OUTPUT, #xmldoc
SELECT * FROM OPENXML (#idoc, '/ROOT', 3)
WITH
(
GuestID smallint 'GUEST/GuestID',
GuestFirst varchar(30) 'GUEST/GuestFirst',
GuestLast varchar(30) 'GUEST/GuestLast',
CheckinDate smalldatetime 'GUEST/RESERVATIONDETAIL/CheckInDate',
Nights tinyint 'GUEST/RESERVATIONDETAIL/Nights'
)
EXEC sp_xml_removedocument #idoc
GO
Instead xml document try it with xquery,
DECLARE #xmldoc XML
Set #xmldoc = '
<ROOT>
<GUEST>
<GuestID>4431</GuestID>
<GuestFirst>Lacey</GuestFirst>
<GuestLast>Byington</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2016-08-02</CheckInDate>
<Nights>2</Nights>
</RESERVATIONDETAIL>
</GUEST>
<GUEST>
<GuestID>5563</GuestID>
<GuestFirst>Jonathan</GuestFirst>
<GuestLast>Langford</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2016-08-05</CheckInDate>
<Nights>2</Nights>
</RESERVATIONDETAIL>
</GUEST>
<GUEST>
<GuestID>6680</GuestID>
<GuestFirst>Tanner</GuestFirst>
<GuestLast>Olson</GuestLast>
<RESERVATIONDETAIL>
<CheckInDate>2015-09-11</CheckInDate>
<Nights>3</Nights>
</RESERVATIONDETAIL>
</GUEST>
</ROOT>'
SELECT
a.b.value('GuestID[1]','smallint') AS GuestID,
a.b.value('GuestFirst[1]','varchar(30)') AS GuestFirst,
a.b.value('GuestLast[1]','varchar(30)') AS GuestLast,
a.b.value('RESERVATIONDETAIL[1]/CheckInDate[1]','smalldatetime') AS CheckInDate,
a.b.value('RESERVATIONDETAIL[1]/Nights[1]','tinyint') AS Nights
FROM #xmldoc.nodes('ROOT/GUEST') AS a(b)
GO
btw, the select query you have given on the top will not produce the same xml which you have given below.
#Jatin answer is good, you can use xquery. You can also use OPENXML like this:
EXEC sp_xml_preparedocument #idoc OUTPUT, #xmldoc
SELECT * FROM OPENXML (#idoc, '/ROOT/GUEST', 3)
WITH
(
GuestID smallint './GuestID',
GuestFirst varchar(30) './GuestFirst',
GuestLast varchar(30) './GuestLast',
CheckinDate smalldatetime './RESERVATIONDETAIL/CheckInDate',
Nights tinyint './RESERVATIONDETAIL/Nights'
)
EXEC sp_xml_removedocument #idoc
You are almost there. You just need to make this small change:
SELECT * FROM OPENXML (#idoc, '/ROOT/*', 3)
WITH
(
GuestID smallint 'GuestID',
GuestFirst varchar(30) 'GuestFirst',
GuestLast varchar(30) 'GuestLast',
CheckinDate smalldatetime 'RESERVATIONDETAIL/CheckInDate',
Nights tinyint 'RESERVATIONDETAIL/Nights'
)

XML Help in TSQL

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

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

Resources