How to ignore XML namespace when creating SQL request? - sql

I have many rows in a DB which contain XML data field. XML approximately looks like this:
<CabasEstimateReply xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://cabmb.cab.se/schemas/CABMBGeneralSchemas/CABASEstimateReply/2006-11-16/">
<Estimate xmlns="">
<WorkshopCompanyId>C002006893</WorkshopCompanyId>
<EstimateId>1-SE-AEB965-634921885183891313</EstimateId>
</Estimate>
<EstimateReply xmlns="">
**<EstimateReplyCode>ReplyStatus1</EstimateReplyCode>**
<EstimateReplyVersion>1</EstimateReplyVersion>
<EstimateReplyDate>2013-05-31T11:40:18.6227322+03:00</EstimateReplyDate>
<EstimateReplyComment />
<EstimateReplyMessage>Kunden betalar : 8692 Fakturaadress : Trygg Hansa</EstimateReplyMessage>
<EstimateReplyMessageCompressMethod />
<EstimateReplyReference>010704</EstimateReplyReference>
<EstimateReplyForthcomingInspectionDate />
</EstimateReply>
<Vehicle xmlns="">
<VehicleRegNo>XND108</VehicleRegNo>
<VehicleMake>BMW</VehicleMake>
<VehicleModel>525I TOURING</VehicleModel>
<VehicleModelYear />
<VehicleModelMonth />
<VehicleVINCode />
<VehicleChassiNo>NL51010CM95684</VehicleChassiNo>
<VehicleFirstRegistered>2006-02-23T00:00:00</VehicleFirstRegistered>
<Imported>null</Imported>
</Vehicle>
I need to have a possibility to get a value EstimateReplyCode(marked with bold) via SQL request. I'm doing this like:
;WITH XMLNAMESPACES(DEFAULT 'https://cabmb.cab.se/schemas/CABMBGeneralSchemas/CABASEstimateReply/2006-11-16/')
select [Data],
Data.value('(/CabasEstimateReply/EstimateReply/EstimateReplyCode)[1]', 'nvarchar(64)') AS ReplyCode
from EstimateReplyRawData
But get only null values for ReplyCode. When I tried to convert XML to string, then replace namespaces and then convert to XML back everything worked well, that's why I suppose that the issue is the namespace. What am I doing wrong here?

If you really want to ignore namespaces, you can use namespace wildcards.
select [Data],
Data.value('(/*:CabasEstimateReply/*:EstimateReply/*:EstimateReplyCode)[1]', 'nvarchar(64)') AS ReplyCode
from EstimateReplyRawData

Related

Retrieving All instances of an 3rd level XML field from an XML column

I have an XML data field in one of my tables that essentially looks like this:
<App xmlns='http://Namespace1'>
<Package xmlns='http://Namespace2'>
<Item>
<ItemDetails xmlns='http://Namespace3'>
<ItemName>ItemNameValue</ItemName>
</ItemDetails>
other_item_stuff
</Item>
<Item>
<ItemDetails>
<ItemName>ItemNameValue</ItemName>
</ItemDetails>
</Item>
...
</Package>
</App>
I need to get all of the ItemNameValues from the XML.
I have tried to adapt many examples found on the web to my purpose, but have failed miserably. The best I seem to be able to do is get one ItemName per Package.
I think that CROSS APPLY is where I need to go, but the syntax to retrieve all the itemdetail.itemname eludes me.
This is my latest failure (returns nothing):
WITH XMLNAMESPACES(
'http://Namespace1' AS xsd,
'http://www.w3.org/2001/XMLSchema-instance' AS xsi,
'http://Namespace2' AS ns1,
'http://Namespace3' AS ns2)
Items.d.value('(ns2:ItemDetails/ItemName/text())[1]','varchar(200)') as
ItemName
FROM MyTable
CROSS APPLY XMLDataColumn.nodes('/xsd:App/ns1:Package/ns1:Item') Items(d)
I hope to get several records from each XML field, but can only ever get the first element.
The biggest problem in this issue is the XML itself:
<App xmlns="http://Namespace1">
<Package xmlns="http://Namespace2">
<Item>
<ItemDetails xmlns="http://Namespace3">
<ItemName>ItemNameValue</ItemName>
</ItemDetails>
other_item_stuff
</Item>
<Item>
<ItemDetails>
<ItemName>ItemNameValue</ItemName>
</ItemDetails>
</Item>
...
</Package>
</App>
Two major Problems:
The namespaces are all declared as default namespaces (they do not include a prefix). All nodes within a node share the same default namespace, if there is nothing else stated explicitly.
The first <ItemDetails> is living within namespace http://Namespace3, while the second <ItemDetails> is living within namespace http://Namespace2 (inherited from <Package>)
That means: If you can - by any chance - change the construction of the XML, try to do this first.
If you have to deal with this, you can try this clean, but clumsy approach.
WITH XMLNAMESPACES(
'http://Namespace1' AS ns1,
'http://www.w3.org/2001/XMLSchema-instance' AS xsi,
'http://Namespace2' AS ns2,
'http://Namespace3' AS ns3)
SELECT COALESCE(Items.d.value('(ns2:ItemDetails/ns2:ItemName/text())[1]','varchar(200)')
,Items.d.value('(ns3:ItemDetails/ns3:ItemName/text())[1]','varchar(200)')) AS ItemName
FROM #xml.nodes('/ns1:App/ns2:Package/ns2:Item') Items(d);
Another approach is to use a namespace wildcard, but be aware of ambigous names...
SELECT Items.d.value('(*:ItemDetails/*:ItemName/text())[1]','varchar(200)') AS ItemName
FROM #xml.nodes('/*:App/*:Package/*:Item') Items(d)

Extracting Text Values from XML in SQL

I'm working with SQL data hosted by a 3rd party, and am trying to pull some specific information for reporting. However, some of what I need to parse out is in XML format, and I'm getting stuck. I'm looking for the syntax to pull the text= values only from the XML code.
I believe the code should something like this, but most of the examples I can find online involve simpler XML hierarchy's than what I'm dealing with.
<[columnnameXML].value('(/RelatedValueListBO/Items/RelatedValueListBOItem/text())[1]','varchar(max)')>
Fails to pull any results. I've tried declaring the spacenames as well, but again...I only ever end up with NULL values pulled.
Example XML I'm dealing with:
<RelatedValueListBO xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://tempuri.org/RelatedValueListBOSchema.xsd">
<Items>
<RelatedValueListBOItem groupKey="Response1" text="Response1" selected="true" />
<RelatedValueListBOItem groupKey="Response2" text="Response2" selected="true" />
<RelatedValueListBOItem groupKey="Response3" text="Response3" selected="true" />
</Items>
</RelatedValueListBO>
Ideally I'd like to pull response1; response2; response3 into a single column. Allowing for the fact that multiple responses may exist. I believe I'm getting stuck with the basic code I've been trying due to the namespaces associated to RelatedValueListBO and the fact that what I want is grouped in groupKey, text, and selected, instead of the value I want just being under the Items node.
You have the namespaces defined in your XML, so you need to define them in the XQuery too.
Fast and dirty method is to replace all namespaces with a "*":
SELECT #x.value('(/*:RelatedValueListBO/*:Items/*:RelatedValueListBOItem/#text)[1]','varchar(max)')
To get all responses in a single column you can use:
SELECT
Item.Col.value('./#text','varchar(max)') X
FROM #x.nodes('/*:RelatedValueListBO/*:Items/*:RelatedValueListBOItem') AS Item(Col)
If you need a better performance, you may need to define namespaces properly.
You can use something like this to extract the value of "text" in the first node of RelatedValueListBOItem
SELECT extractvalue(value(rs), '//RelatedValueListBOItem[1]/#text')
FROM TABLE (xmlsequence(extract(sys.xmltype('<RelatedValueListBO>
<Items>
<RelatedValueListBOItem groupKey="Response1" text="Response1"
selected="true" />
<RelatedValueListBOItem groupKey="Response2" text="Response2"
selected="true" />
<RelatedValueListBOItem groupKey="Response3" text="Response3"
selected="true" />
</Items>
</RelatedValueListBO>'),'/RelatedValueListBO/Items'))) rs;

SQL Server Grabbing Value from XML parameter to use in later query

I am really new to SQL Server and stored procedures to begin with. I need to be able to parse an incoming XML file for a specific element's value and compare/save it later in the procedure.
I have a few things stacked against me. One the Element I need is buried deeply inside the document. I have had no luck in searching for it by name using methods similar to this:
select CurrentBOD = c.value('(local-name(.))[1]', 'VARCHAR(MAX)'),
c.value('(.)[1]', 'VARCHAR(MAX)') from #xml.nodes('PutMessage/payload/content/AcknowledgePartsOrder/ApplicationArea/BODId') as BODtable(c)
It always returns null.
So, I am trying something similar to this:
declare #BODtable TABLE(FieldName VARCHAR(MAX),
FieldValue VARCHAR(MAX))
SELECT
FieldName = nodes.value('local-name(.)', 'varchar(50)'),
FieldValue = nodes.value('(.)[1]', 'varchar(50)')
FROM
#xml.nodes('//*') AS BODtable(nodes)
declare #CurrentBOD VARCHAR(36)
set #CurrentBOD = ''
SET #CurrentBOD = (SELECT FieldValue from #BODtable WHERE FieldName = 'BODId')
This provides me the list of node names and values correctly (I test this in a query and BODtable has all elements listed with the correct values), but when I set #CurrentBOD it comes up null.
Am I missing an easier way to do this? Am I messing these two approaches up somehow?
Here is a part of the xml I am parsing for reference:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wsse="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">
<soap:Header>
<payloadManifest xmlns="???">
<c contentID="Content0" namespaceURI="???" element="AcknowledgePartsOrder" version="4.0" />
</payloadManifest>
<wsa:Action>http://www.starstandards.org/webservices/2005/10/transport/operations/PutMessage</wsa:Action>
<wsa:MessageID>uuid:df8c66af-f364-4b8f-81d8-06150da14428</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>???</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsu:Timestamp wsu:Id="Timestamp-bd91e76f-c212-4555-9b23-f66f839672bd">
<wsu:Created>2013-01-03T21:52:48Z</wsu:Created>
<wsu:Expires>2013-01-03T21:53:48Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="???" wsu:Id="???">
<wsse:Username>???</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">???</wsse:Password>
<wsse:Nonce>???</wsse:Nonce>
<wsu:Created>2013-01-03T21:52:48Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<PutMessage xmlns="??????">
<payload>
<content id="???">
<AcknowledgePartsOrder xmlns="???" xmlns:xsi="???" xsi:schemaLocation="??? ???" revision="???" release="???" environment="???n" lang="en-US" bodVersion="???">
<ApplicationArea>
<Sender>
<Component>???</Component>
<Task>???</Task>
<ReferenceId>???</ReferenceId>
<CreatorNameCode>???</CreatorNameCode>
<SenderNameCode>???</SenderNameCode>
<DealerNumber>???</DealerNumber>
<PartyId>???</PartyId>
<LocationId />
<ServiceId />
</Sender>
<CreationDateTime>2013-01-03T21:52:47</CreationDateTime>
<BODId>71498800-c098-4885-9ddc-f58aae0e5e1a</BODId>
<Destination>
<DestinationNameCode>???</DestinationNameCode>
You need to respect the XML namespaces!
First of all, your target XML node <BODId> is inside the <soap:Envelope> and <soap:Body> tags - both need to be included in your selection.
Secondly, both the <PutMessage> as well as the <AcknowledgePartsOrder> nodes appear to have default XML namespaces (those xmlns=.... without a prefix) - and those must be respected when you select your data using XPath.
So assuming that <PutMessage xmlns="urn:pm"> and <AcknowledgePartsOrder xmlns="urn:apo"> (those are just guesses on my part - replace with the actual XML namespaces that you haven't shown use here), you should be able to use this XPath to get what you're looking for:
;WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS soap,
'urn:pm' AS ns, 'urn:apo' AS apo)
SELECT
XC.value('(apo:BODId)[1]', 'varchar(100)')
FROM
#YourXmlVariable.nodes('/soap:Envelope/soap:Body/ns:PutMessage/ns:payload/ns:content/apo:AcknowledgePartsOrder/apo:ApplicationArea') AS XT(XC)
This does return the expected value (71498800-c098-4885-9ddc-f58aae0e5e1a) in my case.

SQL For XML PATH - Redundant xmlns throughout result and how to return a node with an attribute AND value

Removing all the extraneous information from my initial posting and focusing on two items:
The majority of my data is just <element>value</element> but certain parts call out these namespace prefixes:
<componentList>
<InstantiatedBillingRateComponent>
<definition xsi:type="PSPM">
...
</definition>
Looking at this as a model -
How to return XML from SQL Server 2008 that is structured with multiple selections sharing a common parent
Select BillRateType as "#xsi:type"
...
for xml path('definition'),type,elements
which gave me the error:
XML name space prefix 'xsi' declaration is missing for FOR XML column name '#xsi:type'.
So I did some research on the WITH xmlnamespaces clause and added this to the beginning of the query:
WITH xmlnamespaces ('http://www.w3.org/2001/XMLSchema-instance' as xsi, 'http://www.w3.org/2001/XMLSchema' AS xsd )
I received no error but got this:
<definition xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="PSPM">
and received both xmlns: declarations ALL throughout the entire result (looks like every nested level at the XML PATH or Root if called out)
Problem 1: How can I get rid of all the xmlns (except the top level)
Problem 2: If my column called BillCat has the value COBRA how do I achieve this output?
<billingCategory>
<ID xsi:type="xsd:string">COBRA</ID>
<ID xsi:type="xsd:string">BillingCategory</ID>
</billingCategory>
Update: For this problem 2 I am getting closer - I'm unsure how to mix the element attributes and values together.
If I execute this:
WITH xmlnamespaces ('...://www.w3.org/2001/XMLSchema-instance' as xsi, '...://www.w3.org/2001/XMLSchema' AS xsd )
select
top 1
'xsd:string' as "#xsi:type"
,BillCat
from
##chris_global
for xml path('ID'),type,elements,root('billingCategory')
I get:
<billingCategory xmlns:xsd="....://www.w3.org/2001/XMLSchema" xmlns:xsi="...://www.w3.org/2001/XMLSchema-instance">
<ID xsi:type="xsd:string">
<BillCat>COBRA</BillCat>
</ID>
</billingCategory>
but i want:
<billingCategory>
<ID xsi:type="xsd:string">COBRA</ID>
<ID xsi:type="xsd:string">BillingCategory</ID>
</billingCategory>
I keep looking for query samples that show element with an attribute AND element value like above but cannot find one.
Update -
I found something that seems to work from: [can't post]
WITH xmlnamespaces ('http://www.w3.org/2001/XMLSchema-instance' as xsi, 'http://www.w3.org/2001/XMLSchema' AS xsd )
select
top 1
'xsd:string' as "#xsi:type"
,BillCat as 'text()'
from
##chris_global
for xml path('ID'),type,elements,root('billingCategory')
gave me:
<billingCategory xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID xsi:type="xsd:string">COBRA</ID>
</billingCategory>
So I guess I'm just looking for ways to remove the redundant xmlns from everything (short of parsing it out after the fact)

performing calculations with values in xml CLOB which has identical tags using sql

I've got a table (event_archive) and one of the columns(event_xml) has CLOB data in xml format as below. Is there a way to use SQL to summate the values of the "xx" tag? Please help as i'm completely baffled. Even simply extracting the values is a problem as there are 2 "xx" tags within the same root. Thanks in advance.
<?xml version="1.0" encoding="UTF-8"?>
<event type="CALCULATION">
<source_id>INTERNAL</source_id>
<source_participant/>
<source_role/>
<source_start_pos>1</source_start_pos>
<destination_participant/>
<destination_role/>
<event_id>123456</event_id>
<payload>
<cash_point reference="abc12345">
<adv_start>20120907</adv_start>
<adv_end>20120909</adv_end>
<conf>1234</conf>
<profile>3</profile>
<group>A</group>
<patterns>
<pattern id="00112">
<xx>143554.1</xx>
<yyy>96281.6</yyy>
<adv>875</adv>
</pattern>
<pattern id="00120">
<xx>227606.1</xx>
<yyy>97539.8</yyy>
<adv>18181</adv>
</pattern>
</patterns>
</cash_point>
</payload>
</event>
Different databases handle XML differently. There's no standard way of dealing with XML payloads via raw, standard SQL. So you'll need to look at your actual DB implementation to find out what support they have.