Get all the Subchild values using XQUERY-SQL - 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')

Related

SQL Server Group By with Joins

SELECT
[CustomerKey] AS 'Cust #',
CU.[CompanyName] AS 'Company Name',
ISS.InvoiceDate AS 'Invoice Date',
ISS.InvoiceTotal AS 'Invoice Total',
ISNULL(CU.ShopPhone,'') AS 'Company Shop',
ISNULL(CU.CellPhone,'') AS 'Company Cell',
ISNULL(CU.OfficePhone,'') AS 'Company Office',
ISNULL(CF.FirstName, '') AS 'FName',
ISNULL(CF.LastName,'') AS 'LName',
ISNULL(CF.WorkPhone,'') AS 'Contact Work',
ISNULL(CF.CellPhone,'') AS 'Contact Cell',
ISNULL(CF.HomePhone,'') AS 'Contact Home',
ISNULL(CF.EMail,'') AS 'Contact Email',
PSO.OutsidePartsSalespersonName
FROM
[ProfitMaster].[dbo].[vwAC_SSR_Customer] CU with (nolock)
LEFT JOIN
[ProfitMaster].[dbo].[vwAC_SSR_InvoiceSalesSummary] ISS with (nolock) ON CU.CustomerKey = ISS.Customer
JOIN
[ProfitMaster].[dbo].[vwSV_INV_PartsSalesOrder] PSO with (nolock) ON PSO.PartsSalesOrderInvoiceID = ISS.PartsSalesOrderInvoiceID
LEFT JOIN
(SELECT
EntityID, FirstName, LastName, WorkPhone, CellPhone, HomePhone, EMail
FROM
[ProfitMaster].[dbo].[vwGB_CON_ContactFull] with (nolock)
WHERE
EntityID IS NOT NULL
AND FirstName <> ''
AND EntityTypeID = '3'
AND SetDefault = '1'
GROUP BY
EntityID, FirstName, LastName, WorkPhone, CellPhone, HomePhone, EMail) AS CF ON CU.CustomerID = CF.EntityID
WHERE
CU.Inactive = '0'
AND ISS.InvoiceType = 'Parts Order'
AND ISS.InvoiceDate BETWEEN '2017-02-01 00:00:00.000' AND '2017-03-31 3:59:59.000'
AND CU.CustomerBaseBranchID = '1'
AND PSO.OutsidePartsSalespersonName IN ('Dave Freeland', 'Mark Miller', 'Ryan Oaks')
GROUP BY
CU.CustomerKey, CU.[CompanyName],
ISS.InvoiceDate, ISS.InvoiceTotal,
CU.ShopPhone, CU.CellPhone, CU.OfficePhone,
CF.FirstName, CF.LastName, CF.WorkPhone, CF.CellPhone,
CF.HomePhone, CF.EMail, PSO.OutsidePartsSalespersonName
ORDER BY
CU.CompanyName, ISS.InvoiceDate
How can I group this to SUM ISS.InvoiceTotal grouped by CustomerKey?
I keep getting the "is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause." error.
Any ideas?
If you SUM() in the ISS table you can't select from it or add it to the GROUP BY.
SELECT
[CustomerKey] AS 'Cust #',
CU.[CompanyName] AS 'Company Name',
LAST(ISS.InvoiceDate) AS 'Invoice Date',
SUM(ISS.InvoiceTotal) AS 'Invoice Total',
ISNULL(CU.ShopPhone,'') AS 'Company Shop',
ISNULL(CU.CellPhone,'') AS 'Company Cell',
ISNULL(CU.OfficePhone,'') AS 'Company Office',
ISNULL(CF.FirstName, '') AS 'FName',
ISNULL(CF.LastName,'') AS 'LName',
ISNULL(CF.WorkPhone,'') AS 'Contact Work',
ISNULL(CF.CellPhone,'') AS 'Contact Cell',
ISNULL(CF.HomePhone,'') AS 'Contact Home',
ISNULL(CF.EMail,'') AS 'Contact Email',
PSO.OutsidePartsSalespersonName
FROM
[ProfitMaster].[dbo].[vwAC_SSR_Customer] CU with (nolock)
LEFT JOIN
[ProfitMaster].[dbo].[vwAC_SSR_InvoiceSalesSummary] ISS with (nolock) ON CU.CustomerKey = ISS.Customer
JOIN
[ProfitMaster].[dbo].[vwSV_INV_PartsSalesOrder] PSO with (nolock) ON PSO.PartsSalesOrderInvoiceID = ISS.PartsSalesOrderInvoiceID
LEFT JOIN
(SELECT
EntityID, FirstName, LastName, WorkPhone, CellPhone, HomePhone, EMail
FROM
[ProfitMaster].[dbo].[vwGB_CON_ContactFull] with (nolock)
WHERE
EntityID IS NOT NULL
AND FirstName <> ''
AND EntityTypeID = '3'
AND SetDefault = '1'
GROUP BY
EntityID, FirstName, LastName, WorkPhone, CellPhone, HomePhone, EMail) AS CF ON CU.CustomerID = CF.EntityID
WHERE
CU.Inactive = '0'
AND ISS.InvoiceType = 'Parts Order'
AND ISS.InvoiceDate BETWEEN '2017-02-01 00:00:00.000' AND '2017-03-31 3:59:59.000'
AND CU.CustomerBaseBranchID = '1'
AND PSO.OutsidePartsSalespersonName IN ('Dave Freeland', 'Mark Miller', 'Ryan Oaks')
GROUP BY
CU.CustomerKey, CU.[CompanyName],
CU.ShopPhone, CU.CellPhone, CU.OfficePhone,
CF.FirstName, CF.LastName, CF.WorkPhone, CF.CellPhone,
CF.HomePhone, CF.EMail, PSO.OutsidePartsSalespersonName
ORDER BY
CU.CompanyName

XML select and update

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:EligibilityRequest xmlns:ns1="http://newmmis.eohhs.ma.gov/serviceobjects/versions/1.0/mmiscommon" xmlns:ns4="http://newmmis.eohhs.ma.gov/serviceobjects/versions/1.0/eligrequest" xmlns:ns3="http://xmlns.eohhs.ma.gov/newMMIS/ws/2006/05/MemberInfo" xmlns:ns5="http://newmmis.eohhs.ma.gov/serviceobjects/versions/1.0/eligresponse">
<ns4:transactionsource id_source="HIX" id_other="00000004353" id_medicaid="100208425271"/>
<ns4:demographic cde_born_to_st_empl=" " cde_born_to_ma=" " ind_pregnancy=" " pct_income_fpl="125" amt_income_fpl="1556.57" amt_indv_income="1638.5" amt_indv_prem="0" mail_adr_zip_code="02532" mail_adr_state="MA" mail_adr_city="Boston" mail_adr_street_2="APT 56" mail_adr_street_1="112 Main ST" res_adr_zip_code="02532" res_adr_state="MA" res_adr_city="Boston" res_adr_street_2="APT 56" res_adr_street_1="112 Main ST" nam_first="John" nam_last="Father" email="MAMedicaid01#hcentive.com" num_phone_day="8575550056" cde_lang_written="RUSSIN" cde_primary_lang="RUSSIN" cde_disability_stat=" " cde_homeless="N" cde_ethnicity="UNKNOW" cde_race="UNKNOW" cde_citizen="C" num_primary_ssn="278938115" cde_sex="M" dte_birth="1978-09-09"/>
<ns4:case amt_family_prem_assist="0" amt_family_mh_prem="0" amt_family_prem="0" hoh_nam_last="Father" hoh_nam_first="John" cde_case_status="2" num_case="00003235C"/>
<ns4:eligibility cde_office="555" cde_region="58" dte_appl="2014-11-13" family_size="2" amt_gross_income="1556.57" cde_cat="D1" cde_elig_status="4" cde_line="00" dte_end_elig="2014-11-13" dte_begin_elig="2014-11-03"/>
<ns4:eligibility cde_open_reason="01" cde_office="555" cde_region="58" dte_appl="2014-11-13" family_size="2" amt_gross_income="1556.57" cde_cat="D1" cde_elig_status="1" cde_line="00" dte_begin_elig="2014-11-03"/>
</ns4:EligibilityRequest>
If you open this attached XML, you can see there is 2 tags of <ns4:eligibility
I have to first select this last sub string, either by regexp_substr or simple str .. means output should be:
<ns4:eligibility cde_open_reason="01" cde_office="555" cde_region="58" dte_appl="2014-11-13" family_size="2" amt_gross_income="1556.57" cde_cat="D1" cde_elig_status="1" cde_line="00" dte_begin_elig="2014-11-03"/>
Then append this string to another xml. same like this. just append in last.
Oracle Setup:
CREATE TABLE XML_IN ( id INT, xml CLOB );
INSERT INTO XML_IN VALUES ( 1, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:EligibilityRequest xmlns:ns1="http://newmmis.eohhs.ma.gov/serviceobjects/versions/1.0/mmiscommon" xmlns:ns4="http://newmmis.eohhs.ma.gov/serviceobjects/versions/1.0/eligrequest" xmlns:ns3="http://xmlns.eohhs.ma.gov/newMMIS/ws/2006/05/MemberInfo" xmlns:ns5="http://newmmis.eohhs.ma.gov/serviceobjects/versions/1.0/eligresponse">
<ns4:transactionsource id_source="HIX" id_other="00000004353" id_medicaid="100208425271"/>
<ns4:demographic cde_born_to_st_empl=" " cde_born_to_ma=" " ind_pregnancy=" " pct_income_fpl="125" amt_income_fpl="1556.57" amt_indv_income="1638.5" amt_indv_prem="0" mail_adr_zip_code="02532" mail_adr_state="MA" mail_adr_city="Boston" mail_adr_street_2="APT 56" mail_adr_street_1="112 Main ST" res_adr_zip_code="02532" res_adr_state="MA" res_adr_city="Boston" res_adr_street_2="APT 56" res_adr_street_1="112 Main ST" nam_first="John" nam_last="Father" email="MAMedicaid01#hcentive.com" num_phone_day="8575550056" cde_lang_written="RUSSIN" cde_primary_lang="RUSSIN" cde_disability_stat=" " cde_homeless="N" cde_ethnicity="UNKNOW" cde_race="UNKNOW" cde_citizen="C" num_primary_ssn="278938115" cde_sex="M" dte_birth="1978-09-09"/>
<ns4:case amt_family_prem_assist="0" amt_family_mh_prem="0" amt_family_prem="0" hoh_nam_last="Father" hoh_nam_first="John" cde_case_status="2" num_case="00003235C"/>
<ns4:eligibility cde_office="555" cde_region="58" dte_appl="2014-11-13" family_size="2" amt_gross_income="1556.57" cde_cat="D1" cde_elig_status="4" cde_line="00" dte_end_elig="2014-11-13" dte_begin_elig="2014-11-03"/>
<ns4:eligibility cde_open_reason="01" cde_office="555" cde_region="58" dte_appl="2014-11-13" family_size="2" amt_gross_income="1556.57" cde_cat="D1" cde_elig_status="1" cde_line="00" dte_begin_elig="2014-11-03"/>
</ns4:EligibilityRequest>');
CREATE TABLE XML_OUT( id INT, xml CLOB );
INSERT INTO XML_OUT VALUES ( 1, '<test><attribute>existing</attribute></test>' );
Query:
MERGE INTO XML_OUT o
USING XML_IN i ON ( o.id = i.id )
WHEN MATCHED THEN
UPDATE
SET xml = APPENDCHILDXML(
XMLType( o.xml ),
'/test',
EXTRACT(
XMLType( i.xml ),
'//ns4:EligibilityRequest/ns4:eligibility[last()]',
'xmlns:ns4="http://newmmis.eohhs.ma.gov/serviceobjects/versions/1.0/eligrequest"'
)
).getClobVal();
Output:
SELECT * FROM XML_OUT;
ID XML
---------- ----------------------------------------------------------------------
1 <test><attribute>existing</attribute><ns4:eligibility xmlns:ns4="http:
//newmmis.eohhs.ma.gov/serviceobjects/versions/1.0/eligrequest" cde_ope
n_reason="01" cde_office="555" cde_region="58" dte_appl="2014-11-13" fa
mily_size="2" amt_gross_income="1556.57" cde_cat="D1" cde_elig_status="
1" cde_line="00" dte_begin_elig="2014-11-03"/></test>

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;

How can I save data from xml to sql 2008?

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)

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.