Given the following XML in SQL Server, how do I get a value? - sql

Here is my SQL. I cannot seem to get one single value out of this thing. It only works if I remove all of the xmlns attributes.
I think the problem is that this xml contains 2 default namespaces, one attached to the Response element and one attached to the Shipment element.
DECLARE #xml XML
SET #xml = '<TrackResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Response xmlns="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0">
<ResponseStatus>
<Code>1</Code>
<Description>Success</Description>
</ResponseStatus>
<TransactionReference />
</Response>
<Shipment xmlns="http://www.ups.com/XMLSchema/XOLTWS/Track/v2.0">
<InquiryNumber>
<Code>01</Code>
<Description>ShipmentIdentificationNumber</Description>
<Value>1ZA50209234098230</Value>
</InquiryNumber>
<ShipperNumber>A332098</ShipperNumber>
<ShipmentAddress>
<Type>
<Code>01</Code>
<Description>Shipper Address</Description>
</Type>
<Address>
<AddressLine>123 HWY X</AddressLine>
<City>SOMETOWN</City>
<StateProvinceCode>SW</StateProvinceCode>
<PostalCode>20291 1234</PostalCode>
<CountryCode>US</CountryCode>
</Address>
</ShipmentAddress>
<ShipmentWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>0.00</Weight>
</ShipmentWeight>
<Service>
<Code>42</Code>
<Description>UPS GROUND</Description>
</Service>
<Package>
<TrackingNumber>1ZA50209234098230</TrackingNumber>
<PackageServiceOption>
<Type>
<Code>01</Code>
<Description>Signature Required</Description>
</Type>
</PackageServiceOption>
<Activity>
<ActivityLocation>
<Address>
<City>SOMEWHERE</City>
<StateProvinceCode>PA</StateProvinceCode>
<CountryCode>US</CountryCode>
</Address>
</ActivityLocation>
<Status>
<Type>X</Type>
<Description>Damage reported. / Damage claim under investigation.</Description>
<Code>UY</Code>
</Status>
<Date>20120424</Date>
<Time>125000</Time>
</Activity>
<Activity>
<ActivityLocation>
<Address>
<City>SOMEWHERE</City>
<StateProvinceCode>PA</StateProvinceCode>
<CountryCode>US</CountryCode>
</Address>
</ActivityLocation>
<Status>
<Type>X</Type>
<Description>All merchandise discarded. UPS will notify the sender with details of the damage.</Description>
<Code>GY</Code>
</Status>
<Date>20120423</Date>
<Time>115500</Time>
</Activity>
<PackageWeight>
<UnitOfMeasurement>
<Code>LBS</Code>
</UnitOfMeasurement>
<Weight>0.00</Weight>
</PackageWeight>
</Package>
</Shipment>
</TrackResponse>'
select Svc.Dsc.value('(/TrackResponse/Shipment/Service/Description)[1]', 'varchar(25)')
from #xml.nodes('/TrackResponse') as Svc(Dsc)

As #marc_s said, you are ignoring xml namespaces. Here is a sql fiddle example. This gives X, I think that is what you need. Read this article for more. Note : *:TrackResponse[1]/*: in the xpath
--Results: X
declare #xmlTable as table (
xmlData xml
)
insert into #xmlTable
select '<TrackResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Response xmlns="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0">
...
</TrackResponse>'
;with xmlnamespaces(default 'http://www.ups.com/XMLSchema/XOLTWS/Track/v2.0')
select
x.xmlData.value('(/*:TrackResponse[1]/*:Shipment[1]/Package[1]/Activity[1]/Status[1]/Type[1])','varchar(100)') as all_snacks
from #xmlTable x

Two problems:
you're blatantly ignoring the XML namespace that's defined on the <shipment> element
your XQuery expression was a bit off
Try this:
-- define XML namespace
;WITH XMLNAMESPACES('http://www.ups.com/XMLSchema/XOLTWS/Track/v2.0' AS ns)
select
Svc.Dsc.value('(ns:Shipment/ns:Service/ns:Description)[1]', 'varchar(25)')
from
-- this already selects all <TrackResponse> nodes - no need to repeat that in
-- your above call to .value()
#xml.nodes('/TrackResponse') as Svc(Dsc)
Gives me a result of:
UPS GROUND

Related

Import field in XML using SQL not working

i've been trying to import the field GivenName in my example XML but for some reason it's not working, i've been using the following SQL query, i think i'm using the correct field and the correct nodes but i'm not 100% sure about the XMLNameSpaces
Thank you very much in advance for your help
This is the example SQL Query i'm using:
DECLARE #xml XML = (SELECT [Xml] FROM ExampleTable)
;WITH XMLNAMESPACES (DEFAULT 'http://www.opentravel.org/OTA/2003/05','http://www.w3.org/2003/05/soap-envelope' )
select FirstName = ProfileInfo.value('Profiles[1]/ProfileInfo[1]/Profile[1]/Customer[1]/PersonName[1]/#GivenName', 'nvarchar(255)')
FROM #xml.nodes('Envelope/Body/OTA_Example/Info/Infos/ResUser') as T1(Profiles)
outer apply T1.Profiles.nodes('ResUser2') as T2(ProfileInfo)
This is the example XML i'm using for the import:
<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">
<soap2:Header xmlns:htng="http://htng.org/1.3/Header/" xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:wss="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:htnga="http://htng.org/PWSWG/2007/02/AsyncHeaders"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soap2="http://www.w3.org/2003/05/soap-envelope">
<wsa:Action>Example</wsa:Action>
<wsa:ReplyTo>
<wsa:Address>Example2</wsa:Address>
</wsa:ReplyTo>
<htnga:ReplyTo>
<wsa:Address>Example3</wsa:Address>
</htnga:ReplyTo>
<wsa:MessageID>123</wsa:MessageID>
</soap2:Header>
<Body>
<OTA_Example xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.opentravel.org/OTA/2003/05" Version="5.000" >
<Info>
<Infos CreateDateTime="2021-09-20T06:52:40" CreatorID="User">
<UniqueID Type="1" ID="12341251" />
<ResUser>
<ResUser2 ResGuestRPH="1" PrimaryIndicator="true">
<Profiles>
<ProfileInfo>
<Profile ProfileType="1">
<Customer>
<PersonName>
<NamePrefix>Mr.</NamePrefix>
<GivenName>FirstnameTest</GivenName>
<Surname>LastnameTest</Surname>
</PersonName>
</Customer>
</Profile>
</ProfileInfo>
</Profiles>
</ResUser2>
</ResUser>
</Infos>
</Info>
</OTA_Example>
</Body>
</Envelope>
GivenName is not an attribute, so you shouldn't use # for it.
It's unclear why you needed .nodes, it is only needed if there were multiple nodes that needed breaking out into separate rows
You can also select straight out of ExampleTable, you do not need to store it in a variable.
;WITH XMLNAMESPACES (
'http://www.w3.org/2003/05/soap-envelope' AS soap,
DEFAULT 'http://www.opentravel.org/OTA/2003/05')
select FirstName = [XML].value('(soap:Envelope/soap:Body/OTA_Example/Info/Infos/ResUser/ResUser2/Profiles/ProfileInfo/Profile/Customer/PersonName/GivenName/text())[1]', 'nvarchar(255)')
FROM ExampleTable
db<>fiddle

Parsing XML in SQL without a namespace

The code will be self explanatory to the right person for this, but any questions please shout...
Thanks,
Dave
DECLARE #XML XML
SET #XML =
'<?xml version="1.0" encoding="utf-8"?>
<updates>
<versions>
<installer type="A" Xversion="101" iniSizeInBytes="22480" dataSizeInBytes="23396349" msiSizeInBytes="4732928" />
<installer type="B" Yversion="201" iniSizeInBytes="22480" dataSizeInBytes="116687353" msiSizeInBytes="5807616" webconfigModifierSizeInBytes="11800" />
<installer type="A" Xversion="102" iniSizeInBytes="22480" dataSizeInBytes="23396349" msiSizeInBytes="4732928" />
<installer type="B" Yversion="202" iniSizeInBytes="22480" dataSizeInBytes="116687353" msiSizeInBytes="5807616" webconfigModifierSizeInBytes="11800" />
</versions>
<update setNumber="1" XVersion="101" YVersion="201">
<detail Ref="1000">some detail info for 101 and 201</detail>
</update>
<update setNumber="2" XVersion="102" YVersion="202">
<detail Ref="1001">some detail info for 102 and 202</detail>
</update>
</updates>
'
SELECT
r.value('#ref','NVARCHAR(250)') as 'Ref', --This is wrong, but you can probably see i'm wanting the value of Ref, eg 1000 for line 1, 1001 for line 2
t.r.query('./detail').value('.','nvarchar(max)') as 'Detail'
FROM #XML.nodes('/updates/update') AS t(r);
You need detail element in value method to get the value in Ref attribute
SELECT
r.value('#Ref','NVARCHAR(250)') as 'Ref', -- It should be #Ref instead of #ref
r.query('.').value('.','nvarchar(max)') as 'Detail'
FROM #XML.nodes('/updates/update/detail') AS t(r);
Note : Elements and attributes in xml is case sensitive you cannot use #ref in query when xml attribute is Ref
Rextester Demo

XML import into SQL Server

This is my first time working with XML files. I have been able to read the file into a table. Now I am trying to access the data elements to insert / update into ERP.
I am getting stuck using https://learn.microsoft.com/en-us/sql/t-sql/functions/openxml-transact-sql as a guide.
I am only attempting a single retrieval now, trying to keep things simple:
XML:
<TranscriptRequest xmlns="urn:org:pesc:message:TranscriptRequest:v1.0.0">
<TransmissionData>
<DocumentID xmlns="">88895-20170227180832302-ccd</DocumentID>
<CreatedDateTime xmlns="">2017-02-27T18:08:32.303-08:00</CreatedDateTime>
<DocumentTypeCode xmlns="">Request</DocumentTypeCode>
<TransmissionType xmlns="">Original</TransmissionType>
<Source xmlns="">
<Organization>
<DUNS>626927060</DUNS>
<OrganizationName>AVOW</OrganizationName>
</Organization>
</Source>
<Destination xmlns="">
<Organization>
<OPEID>3419</OPEID>
<OrganizationName>Charleston Southern University</OrganizationName>
</Organization>
</Destination>
<DocumentProcessCode xmlns="">PRODUCTION</DocumentProcessCode>
</TransmissionData>
<Request>
<CreatedDateTime xmlns="">2017-02-27T00:00:00.000-08:00</CreatedDateTime>
<Requestor xmlns="">
<Person>
<Birth>
<BirthDate>1985-01-01</BirthDate>
</Birth>
<Name>
<FirstName>Chad</FirstName>
<LastName>test2</LastName>
</Name>
<AlternateName>
<FirstName>Chad</FirstName>
<LastName>Walker</LastName>
<CompositeName>Walker, Chad</CompositeName>
</AlternateName>
<Contacts>
<Address>
<AddressLine>10260 west st</AddressLine>
<City>Denver</City>
<StateProvinceCode>CO</StateProvinceCode>
<PostalCode>80236</PostalCode>
</Address>
<Phone>
<CountryPrefixCode>1</CountryPrefixCode>
<AreaCityCode>303</AreaCityCode>
<PhoneNumber>8152848</PhoneNumber>
</Phone>
<Email>
<EmailAddress>cwalker#parchment.com</EmailAddress>
</Email>
</Contacts>
</Person>
</Requestor>
My SQL:
USE TMSEPRD
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = [XMLData] FROM [dbo].[CSU_Parchment_XMLwithOpenXML]
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT BirthDate
FROM OPENXML(#hDoc, 'ROOT/Request/Requestor/Person/Birth/Birthdate')
WITH
(
BirthDate varchar(20) '../#BirthDate'
)
EXEC sp_xml_removedocument #hDoc
GO
Birthdate remains blank. So far I have tried eliminating the ../ in front of #Birthdate, I have tried removing the #, I have tried removing Birthdate from the path.
Using Xml Data Type Methods
DECLARE #XML XML='<TranscriptRequest xmlns="urn:org:pesc:message:TranscriptRequest:v1.0.0">
<TransmissionData>
<DocumentID xmlns="">88895-20170227180832302-ccd</DocumentID>
<CreatedDateTime xmlns="">2017-02-27T18:08:32.303-08:00</CreatedDateTime>
<DocumentTypeCode xmlns="">Request</DocumentTypeCode>
<TransmissionType xmlns="">Original</TransmissionType>
<Source xmlns="">
<Organization>
<DUNS>626927060</DUNS>
<OrganizationName>AVOW</OrganizationName>
</Organization>
</Source>
<Destination xmlns="">
<Organization>
<OPEID>3419</OPEID>
<OrganizationName>Charleston Southern University</OrganizationName>
</Organization>
</Destination>
<DocumentProcessCode xmlns="">PRODUCTION</DocumentProcessCode>
</TransmissionData>
<Request>
<CreatedDateTime xmlns="">2017-02-27T00:00:00.000-08:00</CreatedDateTime>
<Requestor xmlns="">
<Person>
<Birth>
<BirthDate>1985-01-01</BirthDate>
</Birth>
<Name>
<FirstName>Chad</FirstName>
<LastName>test2</LastName>
</Name>
<AlternateName>
<FirstName>Chad</FirstName>
<LastName>Walker</LastName>
<CompositeName>Walker, Chad</CompositeName>
</AlternateName>
<Contacts>
<Address>
<AddressLine>10260 west st</AddressLine>
<City>Denver</City>
<StateProvinceCode>CO</StateProvinceCode>
<PostalCode>80236</PostalCode>
</Address>
<Phone>
<CountryPrefixCode>1</CountryPrefixCode>
<AreaCityCode>303</AreaCityCode>
<PhoneNumber>8152848</PhoneNumber>
</Phone>
<Email>
<EmailAddress>cwalker#parchment.com</EmailAddress>
</Email>
</Contacts>
</Person>
</Requestor>
</Request>
</TranscriptRequest>';
SQL query:
with xmlnamespaces('urn:org:pesc:message:TranscriptRequest:v1.0.0' as ns)
select t.n.value('BirthDate[1]','date')
from #XML.nodes('ns:TranscriptRequest/ns:Request/Requestor/Person/Birth') t(n);
Namespaces
How is this XML generated? Is this under your control? What makes me wonder are various xmlns="" This is defining empty default namespaces over and over... Allthough there is one default NS xmlns="urn:org:pesc:message:TranscriptRequest:v1.0.0" in the first line, which seems to be correct.
For this namespace issue there are many solutions:
Create this without the empty xmlns - if this is under your control
Use REPLACE and cut away xmlns="" (be aware of the leading blank!) on string level, before you write this into your xml typed column / variable
use a wildcard (*:) for the namespaces of the first two levels
Define a namespace alias for the default and let the empty namespaces be the default for the lower levels
Structure
Your XML seems to be plain 1:1, only 1 source, 1 destination, one reques. And only 1 person within <Request> and again only 1 Name and so on below <Person>. There is a <Contacts> element, which sounds like 1:n, but it seems to include only 1 address, 1 phone... Reading plain 1:1 is easy...
I will use a CTE with two calls in .query() for an easy approach to solve the namespace issue with a wildcard. This allows to read the rest without namespaces:
some examples how to read your elements, the rest is up to you...
WITH levels AS
(
SELECT #xml.query(N'/*:TranscriptRequest/*:TransmissionData/*') AS td
,#xml.query(N'/*:TranscriptRequest/*:Request/*') AS rq
)
SELECT --TransmissionData
td.value(N'(DocumentID/text())[1]',N'nvarchar(max)') AS DocumentID
,td.value(N'(CreatedDateTime/text())[1]',N'datetime') AS td_CreatedDateTime
--more elemenst...
,td.value(N'(Source/Organization/DUNS/text())[1]',N'bigint') AS Source_Organization_DUNS
--more elements...
,td.value(N'(Destination/Organization/OPEID/text())[1]',N'bigint') AS Destination_Organization_OPEID
--Request
,rq.value(N'(CreatedDateTime/text())[1]',N'datetime') AS rq_CreatedDateTime
--Request-Person
,rq.value(N'(Requestor/Person/Name/FirstName/text())[1]',N'nvarchar(max)') AS Requestor_FirstName
--Contacts
,rq.value(N'(Requestor/Person/Contacts/Address/AddressLine/text())[1]',N'nvarchar(max)') AS Requestor_AddressLine
FROM levels

How to convert nested XML into corresponding tables?

I have a complex nested XML (generated from a C# entity graph), for example:
<Customers>
<Customer>
<Id>1</Id>
<Number>12345</Number>
<Addresses>
<Address>
<Id>100</Id>
<Street>my street </street>
<city>London</city>
</Address>
<Address>
<Id>101</Id>
<street>my street 2</street>
<city>Berlin</city>
</Address>
</Addresses>
<BankDetails>
<BankDetail>
<Id>222</Id>
<Iban>DE8439834934939434333</Iban>
</BankDetail>
<BankDetail>
<Id>228</Id>
<Iban>UK1237921391239123213</Iban>
</BankDetail>
</BankDetails>
<Orders>
<Order>
<OrderLine>
</OrderLine>
</Order>
</Orders>
</Customer>
</Customers>
Before saving the above XML data into the actual tables, I need to process it first. For this reason, I created corresponding table types. Each of these table types have an extra column (guid as ROWGUID) so that if I'm processing new data (not yet assigned primary key) I generate a unique key. I use this column to keep the relational integrity between different table types.
What is the SQL syntax to convert the above nested XML to their corresponding tables, keeping in mind that child records must reference the generated parent guid?
Try it like this:
DECLARE #xml XML=
N'<Customers>
<Customer>
<Id>1</Id>
<AccountNumber>12345</AccountNumber>
<Addresses>
<Address>
<Id>100</Id>
<street>my street></street>
<city>London</city>
</Address>
<Address>
<Id>101</Id>
<street>my street></street>
<city>Berlin</city>
</Address>
</Addresses>
<BankDetails>
<BankDetail>
<Id>222</Id>
<Iban>DE8439834934939434333</Iban>
</BankDetail>
<BankDetail>
<Id>228</Id>
<Iban>UK1237921391239123213</Iban>
</BankDetail>
</BankDetails>
<Orders>
<Order>
<OrderLine />
</Order>
</Orders>
</Customer>
</Customers>';
--This query will create a table #tmpInsert with all the data
SELECT cust.value('Id[1]','int') AS CustomerID
,cust.value('AccountNumber[1]','int') AS CustomerAccountNumber
,addr.value('Id[1]','int') AS AddressId
,addr.value('street[1]','nvarchar(max)') AS AddressStreet
,addr.value('city[1]','nvarchar(max)') AS AddressCity
,bank.value('Id[1]','int') AS BankId
,bank.value('Iban[1]','nvarchar(max)') AS BankIban
,ord.value('OrderLine[1]','nvarchar(max)') AS OrderLine
INTO #tmpInsert
FROM #xml.nodes('/Customers/Customer') AS A(cust)
OUTER APPLY cust.nodes('Addresses/Address') AS B(addr)
OUTER APPLY cust.nodes('BankDetails/BankDetail') AS C(bank)
OUTER APPLY cust.nodes('Orders/Order') AS D(ord);
--Here you can check the content
SELECT * FROM #tmpInsert;
--Clean-Up
GO
DROP TABLE #tmpInsert
Once you've got all your data in the table, you can use simple DISTINCT, GROUP BY, if needed ROW_NUMBER() OVER(PARTITION BY ...) to select each set separately for the proper insert.

Validating xml via xsd

I'm new to XSD, so please help. I've created some XML using "for xml path" in SQL Server Management Studio 2008. It looks like:
I've read some literature, where after creating such an xml, I'm supposed to run this:
But it doesn't work. As I know after saving this file in xml and clicking it twice, it should open xml in browser. But it doesn't. What is wrong? primary key is ReferenceCode. I've created xml using this query:
select p.ReferenceCode
,p.LastName
,p.FirstName
,p.BirthDate
,p.BirthPlace
,(
select d.Type
,d.Series
,d.Number
,d.IssueDate
,d.IssueAuthority
from #Document d
where d.ReferenceCode = p.ReferenceCode
for xml path ('Document'),root('Documents'),Type
)
,(
select a.Type
,a.Street
from #Address a
where a.ReferenceCode = p.ReferenceCode
for xml path ('Address'),root('Addresses'),Type
)
,(
select h.Number
from #Phone h
where h.ReferenceCode = p.ReferenceCode
for xml path ('Phone'),root('Phone'),Type
)
from #Person p
for xml path ('Person'),root('Root')
Thanks in advance
<Root>
<Person>
<ReferenceCode>10000007462</ReferenceCode>
<LastName>Артамонова</LastName>
<FirstName>Галина</FirstName>
<BirthDate>1961-07-19</BirthDate>
<BirthPlace>РОССИЙСКАЯ ФЕДЕРАЦИЯ, д. Криуша Староюрьевского р-на Тамбовской обл.</BirthPlace>
<Documents>
<Document>
<Type>21</Type>
<Series>4508</Series>
<Number>685129</Number>
<IssueDate>2006-08-16</IssueDate>
<IssueAuthority>ОВД р-на Чертаново-Центральное г. Москвы,</IssueAuthority>
</Document>
</Documents>
<Addresses>
<Address>
<Type>1</Type>
<Street>Днепропетровская ул</Street>
</Address>
<Address>
<Type>1</Type>
<Street>Декабристов ул</Street>
</Address>
<Address>
<Type>2</Type>
<Street>Днепропетровская ул</Street>
</Address>
<Address>
<Type>2</Type>
<Street>Декабристов ул</Street>
</Address>
</Addresses>
<Phones>
<Phone>
<Number>907-09-33 </Number>
</Phone>
<Phone>
<Number>+7(903)1780367 </Number>
</Phone>
</Phones>
</Person>
</Root>
Thanks a lot, problem was - I can't create xsd with above xml, please help me to create it. Answers were - use another version, it was created and then deleted, encoding is wrong.
I found the answer myself, I needed just headings which are usually put above the xml. That much.