How can I save data from xml to sql 2008? - sql

How can I save data from xml to sql 2008?
SQL table:
[dbo].[Position](
[ID] [int] IDENTITY(1,1) NOT NULL,
[ImoNo] [numeric](8, 0) NOT NULL,
[sid] [numeric](5, 0) NULL,
[VesselName] [nvarchar](20) NULL,
[time] [datetime] NOT NULL,
[lat] [numeric](9, 2) NULL,
[lon] [numeric](9, 2) NULL,
[sog] [numeric](9, 2) NULL,
[cog] [numeric](9, 2) NULL,
[hdg] [numeric](9, 2) NULL,
[eta] [datetime] NULL,
[NextPort] [nvarchar](20) NULL)
XML file:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.fleettracker.de/api/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:GetPositionsResponse>
<body>
<result>Found 2 vessels.</result>
<success>true</success>
<shipsWithPositions xsi:type="ns1:FleettrackerShip">
<ns1:imono>9456159</ns1:imono>
<ns1:sid>780</ns1:sid>
<ns1:name>Trenta</ns1:name>
<ns1:charterShipName>Trenta</ns1:charterShipName>
<ns1:pasttrack>
<lat>1832900</lat>
<lon>7570400</lon>
<timestamp>2014-01-14T08:28:45Z</timestamp>
<orderNumber>0</orderNumber>
<sog>9.5</sog>
<cog>22</cog>
<hdg>22</hdg>
<eta>2014-01-15T12:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1872560</lat>
<lon>7589000</lon>
<timestamp>2014-01-14T07:00:00Z</timestamp>
<orderNumber>1</orderNumber>
<sog>10.8</sog>
<cog>25</cog>
<hdg>25</hdg>
</ns1:pasttrack>
</shipsWithPositions>
<shipsWithPositions xsi:type="ns1:FleettrackerShip">
<ns1:imono>9144055</ns1:imono>
<ns1:sid>789</ns1:sid>
<ns1:name>Vipava</ns1:name>
<ns1:charterShipName>Vipava</ns1:charterShipName>
<ns1:pasttrack>
<lat>1757160</lat>
<lon>7536240</lon>
<timestamp>2014-01-13T19:00:00Z</timestamp>
<orderNumber>2</orderNumber>
<sog>9.4</sog>
<cog>21</cog>
<hdg>21</hdg>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1658200</lat>
<lon>7476480</lon>
<timestamp>2014-01-13T07:00:00Z</timestamp>
<orderNumber>3</orderNumber>
<sog>8.4</sog>
<cog>29</cog>
<hdg>29</hdg>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1630000</lat>
<lon>7455400</lon>
<timestamp>2014-01-13T03:00:03Z</timestamp>
<orderNumber>4</orderNumber>
<sog>8.83</sog>
<cog>34</cog>
<hdg>34</hdg>
<eta>2014-01-15T08:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
</shipsWithPositions>
</body>
</ns1:GetPositionsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I tried with this query, but I get error msg
'XQuery [nodes()]: The names "SOAP-ENV" and "ns1:" do not denote a namespace.'
DECLARE #xml XML
DECLARE #character VARCHAR(MAX)
SELECT #character = x.y
FROM OPENROWSET( BULK 'C:\Users\Nale\Desktop\POS.xml', SINGLE_CLOB ) x(y)
-- Fix up the ampersand
SELECT #xml = REPLACE( #character, '&', '&' )
-- Get the tally information
SELECT
x.y.value('ns1:imono/text())[1]', 'NUMERIC (8,0)') ImoNo,
x.y.value('ns1:sid/text())[1]', 'NUMERIC (5,0)') sid,
x.y.value('ns1:VesselName/text())[1]', 'NVARCHAR (20)') VesselName,
x.y.value('ns1:pasttrack/time/text())[1]', 'DATETIME') time,
x.y.value('ns1:pasttrack/lat/text())[1]', 'NUMERIC (9,2)') lat,
x.y.value('ns1:pasttrack/lon/text())[1]', 'NUMERIC (9,2)') lon,
x.y.value('ns1:pasttrack/sog/text())[1]', 'NUMERIC (9,2)') sog,
x.y.value('ns1:pasttrack/cog/text())[1]', 'NUMERIC (9,2)') cog,
x.y.value('ns1:pasttrack/hdg/text())[1]', 'NUMERIC (9,2)') hdg,
x.y.value('ns1:pasttrack/eta/text())[1]', 'DATETIME') eta,
x.y.value('ns1:pasttrack/NextPort/text())[1]', 'NVARCHAR (20)') NextPort
FROM #xml.nodes('SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:GetPositionsResponse/body/shipsWithPositions') AS x(y)
XML file is on local disk.
Will I use some sql query, or what is the best way to save data from xml to sql table?

Make XML valid
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.fleettracker.de/api/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:GetPositionsResponse>
<body>
<result>Found 2 vessels.</result>
<success>true</success>
<shipsWithPositions xsi:type="ns1:FleettrackerShip">
<ns1:imono>9456159</ns1:imono>
<ns1:sid>780</ns1:sid>
<ns1:name>Trenta</ns1:name>
<ns1:charterShipName>Trenta</ns1:charterShipName>
<ns1:pasttrack>
<lat>1832900</lat>
<lon>7570400</lon>
<timestamp>2014-01-14T08:28:45Z</timestamp>
<orderNumber>0</orderNumber>
<sog>9.5</sog>
<cog>22</cog>
<hdg>22</hdg>
<eta>2014-01-15T12:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1872560</lat>
<lon>7589000</lon>
<timestamp>2014-01-14T07:00:00Z</timestamp>
<orderNumber>1</orderNumber>
<sog>10.8</sog>
<cog>25</cog>
<hdg>25</hdg>
</ns1:pasttrack>
</shipsWithPositions>
<shipsWithPositions xsi:type="ns1:FleettrackerShip">
<ns1:imono>9144055</ns1:imono>
<ns1:sid>789</ns1:sid>
<ns1:name>Vipava</ns1:name>
<ns1:charterShipName>Vipava</ns1:charterShipName>
<ns1:pasttrack>
<lat>1757160</lat>
<lon>7536240</lon>
<timestamp>2014-01-13T19:00:00Z</timestamp>
<orderNumber>2</orderNumber>
<sog>9.4</sog>
<cog>21</cog>
<hdg>21</hdg>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1658200</lat>
<lon>7476480</lon>
<timestamp>2014-01-13T07:00:00Z</timestamp>
<orderNumber>3</orderNumber>
<sog>8.4</sog>
<cog>29</cog>
<hdg>29</hdg>
</ns1:pasttrack>
<ns1:pasttrack>
<lat>1630000</lat>
<lon>7455400</lon>
<timestamp>2014-01-13T03:00:03Z</timestamp>
<orderNumber>4</orderNumber>
<sog>8.83</sog>
<cog>34</cog>
<hdg>34</hdg>
<eta>2014-01-15T08:00:00</eta>
<nextport>KWANGYANG</nextport>
</ns1:pasttrack>
</shipsWithPositions>
</body>
</ns1:GetPositionsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
2. Add namespaces to your query
DECLARE #xml XML
DECLARE #character VARCHAR(MAX)
SELECT #xml = x.y
FROM OPENROWSET( BULK 'C:\Users\Nale\Desktop\POS.xml', SINGLE_CLOB ) x(y)
-- Get the tally information
;WITH XMLNAMESPACES (
'http://www.fleettracker.de/api/1.0' as ns1,
'http://www.w3.org/2001/XMLSchema-instance' AS xsi,
'http://schemas.xmlsoap.org/soap/envelope/' AS e
)
SELECT
x.y.value('(ns1:imono/text())[1]', 'NUMERIC (8,0)') ImoNo,
x.y.value('(ns1:sid/text())[1]', 'NUMERIC (5,0)') sid,
x.y.value('(ns1:VesselName/text())[1]', 'NVARCHAR (20)') VesselName,
x.y.value('(ns1:pasttrack/time/text())[1]', 'DATETIME') time,
x.y.value('(ns1:pasttrack/lat/text())[1]', 'NUMERIC (9,2)') lat,
x.y.value('(ns1:pasttrack/lon/text())[1]', 'NUMERIC (9,2)') lon,
x.y.value('(ns1:pasttrack/sog/text())[1]', 'NUMERIC (9,2)') sog,
x.y.value('(ns1:pasttrack/cog/text())[1]', 'NUMERIC (9,2)') cog,
x.y.value('(ns1:pasttrack/hdg/text())[1]', 'NUMERIC (9,2)') hdg,
x.y.value('(ns1:pasttrack/eta/text())[1]', 'DATETIME') eta,
x.y.value('(ns1:pasttrack/NextPort/text())[1]', 'NVARCHAR (20)') NextPort
FROM #xml.nodes('e:Envelope/e:Body/ns1:GetPositionsResponse/body/shipsWithPositions') AS x(y)

Related

Using 2 CROSS APPLY in XML

I'm using below XML to fetch the ClauseCode but it's not returning any data for the below query.
<Policy>
<Plans>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>3</ClauseCode>
</ProductEndorsementClauses>
</Plan>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>1</ClauseCode>
<ClauseCode>2</ClauseCode>
</ProductEndorsementClauses>
</Plan>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>5</ClauseCode>
<ClauseCode>4</ClauseCode>
</ProductEndorsementClauses>
</Plan>
</Plans>
</Policy>
Here is my query :
select proposaid,
,Col1.value('(/*/ProductEndorsementClauses/ClauseCode)[1]','nvarchar(max)')
from Policy p
CROSS APPLY data.nodes('/*/Plans/Plan') AS Tbl(Col)
CROSS APPLY Tbl.Col.nodes('/ProductEndorsementClauses/ClauseCode') AS TblPec(Col1)
where Col1.value('(ProductEndorsementClauses/ClauseCode)[1]', 'nvarchar(max)') in ('1','3')
For inspiration:
declare #x xml = N'
<Policy>
<Plans>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>3</ClauseCode>
</ProductEndorsementClauses>
</Plan>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>1</ClauseCode>
<ClauseCode>2</ClauseCode>
</ProductEndorsementClauses>
</Plan>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>5</ClauseCode>
<ClauseCode>4</ClauseCode>
</ProductEndorsementClauses>
</Plan>
</Plans>
</Policy>'
select #x
--all clause code values
select t.col.value('.[1]', 'nvarchar(10)')
from #x.nodes('Policy/Plans/Plan/ProductEndorsementClauses/ClauseCode') as t(col);
--only clause code values that have at least a 1, 3 clause code in their parent ProductEndorsementClauses
select t.col.value('.[1]', 'nvarchar(10)')
from #x.nodes('Policy/Plans/Plan/ProductEndorsementClauses[(./ClauseCode/text() = "1") or (./ClauseCode/text() = "3")]/ClauseCode') as t(col);
--result only when exists at least a 1 or 3
select t.col.value('.[1]', 'nvarchar(max)')
from #x.nodes('Policy/Plans/Plan/ProductEndorsementClauses/ClauseCode') as t(col)
where #x.exist('Policy/Plans/Plan/ProductEndorsementClauses/ClauseCode[text() = "1" or text() = "3"]') = 1;
declare #Policy table
(
proposaid int,
data xml
);
insert into #Policy(proposaid, data)
values(1, N'
<Policy>
<Plans>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>3</ClauseCode>
</ProductEndorsementClauses>
</Plan>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>1</ClauseCode>
<ClauseCode>2</ClauseCode>
</ProductEndorsementClauses>
</Plan>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>5</ClauseCode>
<ClauseCode>4</ClauseCode>
</ProductEndorsementClauses>
</Plan>
</Plans>
</Policy>'),
(2, N'
<Policy>
<Plans>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>13</ClauseCode>
</ProductEndorsementClauses>
</Plan>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>11</ClauseCode>
<ClauseCode>12</ClauseCode>
</ProductEndorsementClauses>
</Plan>
<Plan>
<ProductEndorsementClauses>
<ClauseCode>15</ClauseCode>
<ClauseCode>14</ClauseCode>
</ProductEndorsementClauses>
</Plan>
</Plans>
</Policy>');
select *
from #Policy;
select p.proposaid,
t.col.value('.[1]', 'nvarchar(max)')
from #Policy as p
cross apply p.data.nodes('Policy/Plans/Plan/ProductEndorsementClauses/ClauseCode') as t(col)
where p.data.exist('Policy/Plans/Plan/ProductEndorsementClauses/ClauseCode[text() = "1" or text() = "3"]') = 1;

Parsing XML by OpenXML with multiple Parent nodes with multiple child nodes

I have the following XML:
<Report>
<Accounts>
<Account>
<Currency>USD</Currency>
<AccountBalance>45555</AccountBalance>
<Payments>
<PaymentData>
<PaymentCode>502</PaymentCode>
<PaymentAmount currCode="GBP">7000.00000000</PaymentAmount>
</PaymentData>
<PaymentData>
<PaymentCode>501</PaymentCode>
<PaymentAmount currCode="USD">5000.00000000</PaymentAmount>
</PaymentData>
</Payments>
</Account>
<Account>
<Currency>USD</Currency>
<AccountBalance>50000</AccountBalance>
<Payments>
<PaymentData>
<PaymentCode>501</PaymentCode>
<PaymentAmount currCode="USD">5000.00000000</PaymentAmount>
</PaymentData>
</Payments>
</Account>
</Accounts>
</Report>
My SQL Code is parsing this with the following code:
SELECT
[currCode] AS [Currency],
[AccountBalance] AS [AccountBalance],
[PaymentCode] AS [PaymentCode],
[PaymentCurrCode] AS [PaymentCurrCode],
[PaymentAmount] AS [PaymentAmount]
FROM OPENXML(#hDoc, 'Report/Accounts/Account',2)
WITH
(
[currCode] [nchar](3) 'currCode',
[AccountBalance] [decimal](18, 0) 'AccountBalance',
[PaymentCode] [nchar](10) 'Payments/PaymentData/PaymentCode',
[PaymentCurrCode] [nchar](3) 'Payments/PaymentData/PaymentAmount/#currCode',
[PaymentAmount] [decimal](18, 0) 'Payments/PaymentData/PaymentAmount'
)
I am getting the following result:
currCode | AccountBalance | PaymentCode | PaymentCurrCode | PaymentAmount
————————————————————————————————————————————————————————————————————————————————
USD | 45555 | 502 | GBP |7000.00000000
USD | 50000 | 501 | USD |5000.00000000
I am trying to get the multiple paymentdata and multiple account with the same openXml query. How Can is get all the data with the following result:
currCode | AccountBalance | PaymentCode | PaymentCurrCode | PaymentAmount
————————————————————————————————————————————————————————————————————————————————
USD | 45555 | 502 | GBP |7000.00000000
USD | 45555 | 501 | USD |5000.00000000
USD | 50000 | 501 | USD |5000.00000000
This is an up-to-date and state-of-the-art approach with XQuery/XPath methods. The result is the same, just faster and better to read:
DECLARE #XML XML=
'<Report>
<Accounts>
<Account>
<Currency>USD</Currency>
<AccountBalance>45555</AccountBalance>
<Payments>
<PaymentData>
<PaymentCode>502</PaymentCode>
<PaymentAmount currCode="GBP">7000.00000000</PaymentAmount>
</PaymentData>
<PaymentData>
<PaymentCode>501</PaymentCode>
<PaymentAmount currCode="USD">5000.00000000</PaymentAmount>
</PaymentData>
</Payments>
</Account>
<Account>
<Currency>USD</Currency>
<AccountBalance>50000</AccountBalance>
<Payments>
<PaymentData>
<PaymentCode>501</PaymentCode>
<PaymentAmount currCode="USD">5000.00000000</PaymentAmount>
</PaymentData>
</Payments>
</Account>
</Accounts>
</Report>';
SELECT Payment.value('(../../Currency)[1]','nchar(3)') AS currCode
,Payment.value('(../../AccountBalance)[1]','decimal(18,0)') AS AccountBalance
,Payment.value('PaymentCode[1]','nchar(10)') AS PaymentCode
,Payment.value('PaymentAmount[1]/#currCode','nchar(3)') AS PaymentCurrCode
,Payment.value('PaymentAmount[1]','decimal(18,0)') AS PaymentCurrCode
FROM #XML.nodes('Report/Accounts/Account/Payments/PaymentData') AS One(Payment)
This should work for you:
DECLARE #XML XML=
'<Report>
<Accounts>
<Account>
<Currency>USD</Currency>
<AccountBalance>45555</AccountBalance>
<Payments>
<PaymentData>
<PaymentCode>502</PaymentCode>
<PaymentAmount currCode="GBP">7000.00000000</PaymentAmount>
</PaymentData>
<PaymentData>
<PaymentCode>501</PaymentCode>
<PaymentAmount currCode="USD">5000.00000000</PaymentAmount>
</PaymentData>
</Payments>
</Account>
<Account>
<Currency>USD</Currency>
<AccountBalance>50000</AccountBalance>
<Payments>
<PaymentData>
<PaymentCode>501</PaymentCode>
<PaymentAmount currCode="USD">5000.00000000</PaymentAmount>
</PaymentData>
</Payments>
</Account>
</Accounts>
</Report>';
DECLARE #hDoc INT;
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML;
SELECT
[currCode] AS [Currency],
[AccountBalance] AS [AccountBalance],
[PaymentCode] AS [PaymentCode],
[PaymentCurrCode] AS [PaymentCurrCode],
[PaymentAmount] AS [PaymentAmount]
FROM OPENXML(#hDoc, 'Report/Accounts/Account/Payments/PaymentData',2)
WITH
(
[currCode] [nchar](3) '../../Currency',
[AccountBalance] [decimal](18, 0) '../../AccountBalance',
[PaymentCode] [nchar](10) 'PaymentCode',
[PaymentCurrCode] [nchar](3) 'PaymentAmount/#currCode',
[PaymentAmount] [decimal](18, 0) 'PaymentAmount'
)
EXEC sp_xml_removedocument #hDoc;

EXTRACT RESULTS FROM XML node with namespace using SQL

I Have the below XML and want to extract the values for the following Nodes
1. result
2. documentNumber
3. costElementCode
<commitmentsResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<result xmlns="http://response.cim.its.test.edu.au/">SUCCESS</result>
<value xmlns="http://finance.response.cim.its.test.edu.au/">
<documentNumber xmlns="http://finance.cim.its.test.edu.au/">12345</documentNumber>
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
<commitmentLine xmlns="http://finance.cim.its.test.edu.au/">
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
</value>
</commitmentsResponse>
Without using Namespaces:
DECLARE #myXML xml =
N'<commitmentsResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<result>SUCCESS</result>
<value>
<documentNumber>12345</documentNumber>
<commitmentLine>
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
<commitmentLine xmlns="http://finance.cim.its.test.edu.au/">
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
</value>
</commitmentsResponse>'
DECLARE #DocumentNumber INT
SELECT #DocumentNumber = [Table].[Column].value('documentNumber[1]', 'INT')
FROM #myXML.nodes('/commitmentsResponse/value') AS [Table]([Column])
DECLARE #Result VARCHAR(256)
SELECT #Result = [Table].[Column].value('result[1]', 'varchar(256)')
FROM #myXML.nodes('/commitmentsResponse') AS [Table]([Column])
DECLARE #CostElementCode VARCHAR(256)
SELECT #CostElementCode = [Table].[Column].value('costElementCode[1]', 'varchar(256)')
FROM #myXML.nodes('/commitmentsResponse/value/commitmentLine') AS [Table]([Column])
SELECT #Result
SELECT #DocumentNumber
SELECT #CostElementCode
With using namespaces:
DECLARE #myXML xml =
N'<commitmentsResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<result xmlns="http://response.cim.its.test.edu.au/">SUCCESS</result>
<value>
<documentNumber xmlns="http://finance.cim.its.test.edu.au/">12345</documentNumber>
<commitmentLine>
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
<commitmentLine xmlns="http://finance.cim.its.test.edu.au/">
<lineNumber>2</lineNumber>
<costElementCode>costElementCode</costElementCode>
<internalOrderNumber>1000002</internalOrderNumber>
<costCentreCode>9999</costCentreCode>
<wbsCode>3000</wbsCode>
<lineDescription>2 packets of pencils</lineDescription>
<accountNumber>100000</accountNumber>
<itemAmount>105.5</itemAmount>
<fundsDueDate>2015-06-15</fundsDueDate>
</commitmentLine>
</value>
</commitmentsResponse>'
DECLARE #DocumentNumber INT
;WITH XMLNAMESPACES (N'http://finance.cim.its.test.edu.au/' as DYN)
SELECT #DocumentNumber = c.value('(DYN:documentNumber)[1]', 'INT')
FROM #myXML.nodes('/commitmentsResponse/value') t(c)
DECLARE #Result VARCHAR(256)
;WITH XMLNAMESPACES (N'http://response.cim.its.test.edu.au/' as DYN)
SELECT #Result = c.value('(DYN:result)[1]', 'VARCHAR(256)')
FROM #myXML.nodes('/commitmentsResponse') t(c)
DECLARE #CostElementCode VARCHAR(256)
SELECT #CostElementCode = c.value('(costElementCode)[1]', 'VARCHAR(256)')
FROM #myXML.nodes('/commitmentsResponse/value/commitmentLine') t(c)
SELECT #Result
SELECT #DocumentNumber
SELECT #CostElementCode

How to control the output format of XML in SQL

I'm trying to retrieve data from a (SQL Server 2008 R2) table in XML format and can't seem to get it into an ideal structure. I've tried various FOR XML modes and join methods, but I have minimal experience with XML and have not worked with XML output via SQL prior to this.
My data is in a table named XMLResults with the following structure:
ResultID Country Product Disposition Results
-----------------------------------------------
1 UK ABC Result1 100
2 UK ABC Result2 200
3 UK ABC Result3 300
4 UK XYZ Result1 100
5 UK XYZ Result2 200
6 UK XYZ Result3 300
7 USA ABC Result1 100
8 USA ABC Result2 200
9 USA ABC Result3 300
10 USA XYZ Result1 100
11 USA XYZ Result2 200
12 USA XYZ Result3 300
Currently I have this query:
SELECT (SELECT Country,
(SELECT Product,
(SELECT Disposition, Results
FROM XMLResults t1
WHERE t1.ResultID = t2.ResultID
FOR XML PATH ('Dispositions'), TYPE, ELEMENTS
)
FROM XMLResults t2
WHERE t2.ResultID = t3.ResultID
FOR XML PATH ('Products'), TYPE, ELEMENTS
)
FROM XMLResults t3
ORDER BY Country, Product
FOR XML PATH ('Countries'), TYPE, ELEMENTS
)
FOR XML PATH('Stats');
Which returns the XML like this:
<Stats>
<Countries>
<Country>UK</Country>
<Products>
<Product>ABC</Product>
<Dispositions>
<Disposition>Result1</Disposition>
<Results>100</Results>
</Dispositions>
</Products>
</Countries>
<Countries>
<Country>UK</Country>
<Products>
<Product>ABC</Product>
<Dispositions>
<Disposition>Result2</Disposition>
<Results>200</Results>
</Dispositions>
</Products>
</Countries>
<Countries>
<Country>UK</Country>
<Products>
<Product>ABC</Product>
<Dispositions>
<Disposition>Result3</Disposition>
<Results>300</Results>
</Dispositions>
</Products>
</Countries>
...
</Stats>
Not terrible, but I'd prefer it not drop all the way back out to the 'Countries' level after each result, and probably don't need the extra generic tags either.
Something like this would be better:
<Stats>
<Countries>
<Country = "UK">
<Products>
<Product = "ABC">
<Dispositions>
<Disposition>
<ReasonCode>Result1</ReasonCode>
<Count>100</Count>
</Disposition>
<Disposition>
<ReasonCode>Result2</ReasonCode>
<Count>200</Count>
</Disposition>
<Disposition>
<ReasonCode>Result3</ReasonCode>
<Count>300</Count>
</Disposition>
</Dispositions>
</Product>
...
</Products>
</Country>
...
</Countries>
</Stats>
Maybe even something like this (looks a little bit cleaner):
<Stats>
<Country = "UK">
<Product = "ABC">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
<Product = "XYZ">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
</Country>
<Country = "USA">
<Product = "ABC">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
<Product = "XYZ">
<Disposition ReasonCode = "Result1" Count = "100" />
<Disposition ReasonCode = "Result2" Count = "200" />
<Disposition ReasonCode = "Result3" Count = "300" />
</Product>
</Country>
</Stats>
The output format isn't set in stone, so if there are recommendations, I'm open to suggestions on that as well.
Thanks,
Sean
Edit for sample data:
CREATE TABLE XMLResults (
ResultID BIGINT IDENTITY(1,1) NOT NULL
, Country VARCHAR(50) NOT NULL
, Product VARCHAR(50) NOT NULL
, Disposition VARCHAR(50) NOT NULL
, Results INT NOT NULL);
INSERT INTO XMLResults (Country, Product, Disposition, Results)
VALUES ('UK', 'ABC', 'Result1', 100)
, ('UK', 'ABC', 'Result2', 200)
, ('UK', 'ABC', 'Result3', 300)
, ('UK', 'XYZ', 'Result1', 100)
, ('UK', 'XYZ', 'Result2', 200)
, ('UK', 'XYZ', 'Result3', 300)
, ('USA', 'ABC', 'Result1', 100)
, ('USA', 'ABC', 'Result2', 200)
, ('USA', 'ABC', 'Result3', 300)
, ('USA', 'XYZ', 'Result1', 100)
, ('USA', 'XYZ', 'Result2', 200)
, ('USA', 'XYZ', 'Result3', 300);
If you want attribute-centric xml, prepend '#' to your column aliases. You can completely customize the output, e.g:
SELECT
Country [#Country],
(SELECT
Product [#Product],
(SELECT
Disposition [#ReasonCode],
Results [#Count]
FROM #XMLResults t3
WHERE t3.Country = t1.Country AND t3.Product = t2.Product
FOR XML PATH('Disposition'),TYPE)
FROM #XMLResults t2
WHERE t2.Country = t1.Country
GROUP BY Product
FOR XML PATH('Product'),TYPE)
FROM #XMLResults t1
GROUP BY Country
FOR XML PATH('Country'), ROOT ('Stats')
Just play around with it.

Get all the Subchild values using XQUERY-SQL

I'm attempting to create an xquery expression that will return all the subchild node values. Now I get only the first subchild under each child.
I have the following XML:
<order>
<child1>
<subchild id="S1">
<name type="primary">
<first>NAOMI</first>
<middle />
<last>ADAMS</last>
<suffix />
</name>
</subchild>
<subchild id="S2">
<name type="primary">
<first>TOVER</first>
<middle />
<last>DALI</last>
<suffix />
</name>
</subchild>
</child1>
<child2>
<subchild id="V1">
<year>2002</year>
<make>PONTI</make>
<model>AZTEK</model>
<vin>3G7DA03E32S597676</vin>
</subchild>
<subchild id="V2">
<year>2003</year>
<make>HONDA</make>
<model>CIVIC</model>
<vin>2H94DA03E80S1538</vin>
</subchild>
</child2>
<child3>
<subchild id="A1">
<house>7741</house>
<street1>SAINT BERNARD ST</street1>
<apartment />
<city>PLAYA DEL REY</city>
<state>CA</state>
<postalcode>90293</postalcode>
</subchild>
<subchild id="A2">
<house>2371</house>
<street1>HANNUM DR</street1>
<apartment />
<city>MARINA DEL REY</city>
<state>CA</state>
<postalcode>90293</postalcode>
</subchild>
</child3>
</order>
My result should look like :
FirstName LastName HouseNumber StreetName City SState Zip Year Model Make VIN
NAOMI ADAMS 7741 SAINT BERNARD ST PLAYA DEL REY CA 90293 2002 PONTI AZTEK G7DA03E32S597676
TOVER DALI 2371 HANNUM DR MARINA DEL REY CA 90024 2003 HONDA CIVIC 2H94DA03E80S1538
My query :
SELECT
FirstName, LastName, HouseNumber, StreetName, City, SState, Zip, Year, Model, Make, VIN
FROM
xmlTable
OUTER APPLY
(
SELECT
tbl.col.value('(child1/subchild/name/first)[1]','varchar(20)') AS FirstName,
tbl.col.value('(child1/subchild/name/last)[1]','varchar(20)') AS LastName,
tbl.col.value('(child1/subchild/name/middle)[1]','varchar(20)') AS MiddleName,
tbl.col.value('(child3/subchild/house)[1]','varchar(20)') AS HouseNumber,
tbl.col.value('(child3/subchild/street1)[1]','varchar(20)') AS StreetName,
tbl.col.value('(child3/subchild/city)[1]','varchar(20)') AS City,
tbl.col.value('(child3/subchild/state)[1]','varchar(20)') AS SState,
tbl.col.value('(child3/subchild/postalcode)[1]','varchar(20)') AS Zip,
tbl.col.value('(child2/subchild/model_year)[1]','varchar(20)') AS Year,
tbl.col.value('(child2/subchild/model)[1]','varchar(20)') AS Model,
tbl.col.value('(child2/subchild/make)[1]','varchar(20)') AS Make,
tbl.col.value('(child2/subchild/vin)[1]','varchar(20)') AS VIN
FROM
xmldocument.nodes('//order') AS tbl(col)
) Y
Thanks
DECLARE #x XML
SELECT #x = 'Your XML here'
SELECT
tbl.col.value('(child1/subchild/name/first)[position() = sql:column("s.number")][1]','varchar(20)') AS FirstName,
tbl.col.value('(child1/subchild/name/last)[position() = sql:column("s.number")][1]', 'varchar(20)') AS LastName,
tbl.col.value('(child1/subchild/name/middle)[position() = sql:column("s.number")][1]', 'varchar(20)') AS MiddleName,
tbl.col.value('(child3/subchild/house)[position() = sql:column("s.number")][1]', 'varchar(20)') AS HouseNumber,
tbl.col.value('(child3/subchild/street1)[position() = sql:column("s.number")][1]', 'varchar(20)') AS StreetName,
tbl.col.value('(child3/subchild/city)[position() = sql:column("s.number")][1]', 'varchar(20)') AS City,
tbl.col.value('(child3/subchild/state)[position() = sql:column("s.number")][1]', 'varchar(20)') AS SState,
tbl.col.value('(child3/subchild/postalcode)[position() = sql:column("s.number")][1]', varchar(20)') AS Zip,
tbl.col.value('(child2/subchild/model_year)[position() = sql:column("s.number")][1]', 'varchar(20)') AS Year,
tbl.col.value('(child2/subchild/model)[position() = sql:column("s.number")][1]', 'varchar(20)') AS Model,
tbl.col.value('(child2/subchild/make)[position() = sql:column("s.number")][1]', 'varchar(20)') AS Make,
tbl.col.value('(child2/subchild/vin)[position() = sql:column("s.number")][1]', 'varchar(20)') AS VIN
FROM #x.nodes('/order') AS tbl(col)
CROSS JOIN master..spt_values s
WHERE type = 'P' AND number BETWEEN 1
AND col.query('count(child1/subchild)').value('.', 'INT')