Multiple values from XML column - sql-server

I am trying to figure out how to get multiple values from multiple nodes of an XML field in a table (actually it's XML stored as text).
I've seen several methods that involve declaring the XML as a variable and using it as a table but I don't see how that would work for me. How to Extract data from xml column in sql 2008
I am currently using .value to get some fields but I don't see how to make it work since there can be multiple LX01_AssignedNumber and I need to get all of the ProcedureModifier from each.
SELECT CAST(xmldata as xml).value('declare namespace ns1="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006/EnrichedMessageXML";declare namespace ns0="http://schemas.microsoft.com/BizTalk/EDI/X12/2006";
(/ns1:X12EnrichedMessage/TransactionSet/ns0:X12_00501_837_P/ns0:TS837_2000A_Loop/ns0:TS837_2000B_Loop/ns0:TS837_2300_Loop/ns0:TS837_2400_Loop/ns0:SV1_ProfessionalService/ns0:C003_CompositeMedicalProcedureIdentifier/C00303_ProcedureModifier) [1]', 'varchar(20)') AS RendAttendNPI
FROM EDI_DATA
How do I get all the Line Numbers and all of the Procedure Modifiers from each record?
XML:
<ns1:X12EnrichedMessage xmlns:ns1="http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006/EnrichedMessageXML">
...
<TransactionSet>
<!-- ProcessLogID=PLG0005169955 ;ProcessLogDetailID=PLG0005173285 ;EnvID=1;RetryCount=1 -->
<ns0:X12_00501_837_P xmlns:ns0="http://schemas.microsoft.com/BizTalk/EDI/X12/2006">
<ns0:TS837_2000A_Loop xmlns:ns0="http://schemas.microsoft.com/BizTalk/EDI/X12/2006">
<ns0:TS837_2000B_Loop xmlns:ns0="http://schemas.microsoft.com/BizTalk/EDI/X12/2006">
<ns0:TS837_2300_Loop xmlns:ns0="http://schemas.microsoft.com/BizTalk/EDI/X12/2006">
<ns0:TS837_2400_Loop>
<ns0:LX_ServiceLineNumber>
<LX01_AssignedNumber>1</LX01_AssignedNumber>
</ns0:LX_ServiceLineNumber>
<ns0:SV1_ProfessionalService>
<ns0:C003_CompositeMedicalProcedureIdentifier>
<C00301_ProductorServiceIDQualifier>HC</C00301_ProductorServiceIDQualifier>
<C00302_ProcedureCode>26340</C00302_ProcedureCode>
<C00303_ProcedureModifier>AG</C00303_ProcedureModifier>
<C00304_ProcedureModifier>58</C00304_ProcedureModifier>
<C00305_ProcedureModifier>51</C00305_ProcedureModifier>
<C00306_ProcedureModifier>XS</C00306_ProcedureModifier>
</ns0:C003_CompositeMedicalProcedureIdentifier>
<SV102_LineItemChargeAmount>8918</SV102_LineItemChargeAmount>
<SV103_UnitorBasisforMeasurementCode>UN</SV103_UnitorBasisforMeasurementCode>
<SV104_ServiceUnitCount>13</SV104_ServiceUnitCount>
<ns0:C004_CompositeDiagnosisCodePointer>
<C00401_DiagnosisCodePointer>1</C00401_DiagnosisCodePointer>
<C00402_DiagnosisCodePointer>2</C00402_DiagnosisCodePointer>
</ns0:C004_CompositeDiagnosisCodePointer>
</ns0:SV1_ProfessionalService>
<ns0:DTP_SubLoop_2>
<ns0:DTP_Date_ServiceDate>
<DTP01_DateTimeQualifier>472</DTP01_DateTimeQualifier>
<DTP02_DateTimePeriodFormatQualifier>D8</DTP02_DateTimePeriodFormatQualifier>
<DTP03_ServiceDate>20160104</DTP03_ServiceDate>
</ns0:DTP_Date_ServiceDate>
</ns0:DTP_SubLoop_2>
<ns0:REF_SubLoop_7>
<ns0:REF_LineItemControlNumber>
<REF01_ReferenceIdentificationQualifier>6R</REF01_ReferenceIdentificationQualifier>
<REF02_LineItemControlNumber>11453481</REF02_LineItemControlNumber>
</ns0:REF_LineItemControlNumber>
</ns0:REF_SubLoop_7>
</ns0:TS837_2400_Loop>
<ns0:TS837_2400_Loop>
<ns0:LX_ServiceLineNumber>
<LX01_AssignedNumber>2</LX01_AssignedNumber>
</ns0:LX_ServiceLineNumber>
<ns0:SV1_ProfessionalService>
<ns0:C003_CompositeMedicalProcedureIdentifier>
<C00301_ProductorServiceIDQualifier>HC</C00301_ProductorServiceIDQualifier>
<C00302_ProcedureCode>20680</C00302_ProcedureCode>
<C00303_ProcedureModifier>58</C00303_ProcedureModifier>
</ns0:C003_CompositeMedicalProcedureIdentifier>
<SV102_LineItemChargeAmount>1277</SV102_LineItemChargeAmount>
<SV103_UnitorBasisforMeasurementCode>UN</SV103_UnitorBasisforMeasurementCode>
<SV104_ServiceUnitCount>1</SV104_ServiceUnitCount>
<ns0:C004_CompositeDiagnosisCodePointer>
<C00401_DiagnosisCodePointer>3</C00401_DiagnosisCodePointer>
</ns0:C004_CompositeDiagnosisCodePointer>
</ns0:SV1_ProfessionalService>
</ns0:TS837_2400_Loop>
</ns0:TS837_2300_Loop>
</ns0:TS837_2000B_Loop>
</ns0:TS837_2000A_Loop>
</ns0:X12_00501_837_P>
</TransactionSet>
</ns1:X12EnrichedMessage>

Look into SQL Server CROSS APPLY which you can use to shred single XML data into multiple rows, for example :
;WITH XMLNAMESPACES ('http://schemas.microsoft.com/BizTalk/EDI/X12/2006' as ns0
,'http://schemas.microsoft.com/BizTalk/EDI/EDIFACT/2006/EnrichedMessageXML' as ns1)
SELECT
TS837_2400_Loop.value('(.//LX01_AssignedNumber)[1]', 'int') 'line_number'
,C00303_ProcedureModifier.value('.', 'varchar(100)') 'procedure_modifier'
FROM EDI_DATA
CROSS APPLY (select CONVERT(XML, xmldata)) as P(X)
CROSS APPLY X.nodes('.//ns0:TS837_2400_Loop') AS Q(TS837_2400_Loop)
CROSS APPLY TS837_2400_Loop.nodes('.//C00303_ProcedureModifier') AS R(C00303_ProcedureModifier)
sqlfiddle demo
output :
| line_number | procedure_modifier |
|-------------|--------------------|
| 1 | AG |
| 2 | 58 |

Related

How do I select the value of an ID element in XPath?

Given an XML with the following set up:
<eCrow.CrowGroup CorrelationID="ec367934-e7bd-4213-b0e5-d149c57eec61" >
<eCrow.01>fu</eCrow.01>
<eCrow.02>bar</eCrow.02>
<eCrow.03 CorrelationID="bfe7d35b-bbc1-4591-8d0d-9d42252039bc" >03003</eCrew.03>
</eCrow.CrowGroup>
How do I manage to get XPath to select the CorrelationID from within the node header: <eCrow.CrowGroup CorrelationID="ec367934-e7bd-4213-b0e5-d149c57eec61" >, NOT the CorrelationID from eCrow.03.
In regards to the link suggestion, I am probably doing something wrong but //eCrew.CrewGroup/*#CorrelationID just selects the entire node.
Please try the following.
As already mentioned in the comments, I had to fix your XML to make it well-formed.
XQuery .value() method gives you the answer.
SQL
DECLARE #xml XML =
N'<eCrow.CrowGroup CorrelationID="ec367934-e7bd-4213-b0e5-d149c57eec61">
<eCrow.01>fu</eCrow.01>
<eCrow.02>bar</eCrow.02>
<eCrow.03 CorrelationID="bfe7d35b-bbc1-4591-8d0d-9d42252039bc">03003</eCrow.03>
</eCrow.CrowGroup>';
SELECT #xml.value('(/eCrow.CrowGroup/eCrow.03/#CorrelationID)[1]', 'VARCHAR(30)') AS CorrelationID
, #xml.value('(/eCrow.CrowGroup/eCrow.03/#CorrelationID)[1]', 'uniqueidentifier') AS CorrelationID2;
Output
+--------------------------------+--------------------------------------+
| CorrelationID | CorrelationID2 |
+--------------------------------+--------------------------------------+
| bfe7d35b-bbc1-4591-8d0d-9d4225 | BFE7D35B-BBC1-4591-8D0D-9D42252039BC |
+--------------------------------+--------------------------------------+

Selecting data from XML

I'm trying to insert a row based on data extracted from a chunk of XML. Some columns need to initialized to node values a couple of nodes "deep" in the XML structure.
I can't seem to the query right. Here's what I got:
declare #xmlRaw xml = '
<LogEntry>
<SummaryMessage>Something bad happened</SummaryMessage>
<Exception>
<Type>System.ApplicationException</Type>
<Message>A test of the error handling</Message>
</Exception>
</LogEntry>'
select
LogEntryColumn.value('SummaryMessage[1]', 'varchar(10)') as SummaryMessage, -- works fine
LogEntryColumn.query('Exception[1]').value('Message[1]', 'varchar(10)') as ExMessage -- not working
from
#xmlRaw.nodes('LogEntry[1]') as LogEntryTable(LogEntryColumn)
This outputs:
SummaryMessage ExMessage
-------------- ----------
Something NULL
I've tried a raft of variations for the "ExMessage" column query but no joy.
Note that I'm using "LogEntryColumn.query(...).value(...)" because I want to check how that form performs versus something like:
select
LogEntryColumn.value('SummaryMessage[1]', 'varchar(10)') as SummaryMessage, -- works fine
ExceptionEntryColumn.value('Message[1]', 'varchar(10)') as ExMessage -- not working
from
#xmlRaw.nodes('LogEntry[1]') as LogEntryTable(LogEntryColumn)
outer apply #xmlData.nodes('LogEntry[1]/Exception') as ExceptionTable(ExceptionEntryColumn)
Basically I'm wondering if multiple "outer apply" from clauses is better/worse than multiple .query(...) invocations.
Here is what you need.
SQL
DECLARE #xmlRaw XML =
N'<LogEntry>
<SummaryMessage>Something bad happened</SummaryMessage>
<Exception>
<Type>System.ApplicationException</Type>
<Message>A test of the error handling</Message>
</Exception>
</LogEntry>';
SELECT c.value('(SummaryMessage/text())[1]', 'varchar(100)') AS SummaryMessage
, c.value('(Exception/Message/text())[1]', 'varchar(100)') AS ExMessage
FROM #xmlRaw.nodes('/LogEntry') AS t(c);
Output
+------------------------+------------------------------+
| SummaryMessage | ExMessage |
+------------------------+------------------------------+
| Something bad happened | A test of the error handling |
+------------------------+------------------------------+

Extracting XML field in SQL Server

I have fldxml column in a MIWOD table that contains multiple type of data. How can I display [fldxml] column value as shown below?
Routing Drawing
1 C:\Users\XXX\Documents\LETTUCE_WHEEL.pdf
Here is how [fldxml] looks like
<fields>
<field1>1</field1>
<field2>C:\Users\XXX\Documents\LETTUCE_WHEEL.pdf</field2>
</fields>
I want get something like this
Routing Drawing:
1 C:\Users\XXX\Documents\LETTUCE_WHEEL.pdf
I tried using the following
SELECT
MIWOD.fldXml('(field1/text())[1]', 'varchar(50)') as Routing,
MIWOD.fldXml('(field1/text())[1]', 'varchar(50)') as Routing
FROM
[MISAMPCO].[dbo].[MIWOD]
But I get the following error
Cannot find either column "MIWOD" or the user-defined function or aggregate "MIWOD.fldXml", or the name is ambiguous.
DECLARE #stuff xml
SET #stuff = '<fields><field1>1</field1><field2>C:\Users\Kinfe\Documents\LETTUCE_WHEEL.pdf</field2></fields>'
SELECT
Child.value('field1[1]', 'int') [**Routing:**],
Child.value('field2[1]', 'nvarchar(max)') [**Drawing:**]
FROM
#stuff.nodes('fields[1]') as N(Child)

Shred XML file into SQL Server table

I have researched the best way to shred this xml file extensively and have come close but not all the way to what I want.
I am using SQL Server 2012 and have Visual Studio 2012 as well though I prefer to use SQL Server.
Here is a snippet of the type of XML data I am working with. I cannot control how the XML is built as it comes from a 3rd party. In Reality below the node there are about 450 response types such as ResponseID, Name, Status etc... I only show about ten.
<xml>
<Response>
<ResponseID>R_a4yThVvKXzVyftz</ResponseID>
<ResponseSet>Default Response Set</ResponseSet>
<Name>Doe, John</Name>
<ExternalDataReference>0</ExternalDataReference>
<EmailAddress>jdoe#gmail.com</EmailAddress>
<IPAddress>140.123.12.123</IPAddress>
<Status>0</Status>
<StartDate>2014-09-18 09:21:11</StartDate>
<EndDate>2014-09-23 16:09:58</EndDate>
<Finished>1</Finished>
</Response>
</xml>
I've tried the OPENROWSET Method shown on this site
http://blogs.msdn.com/b/simonince/archive/2009/04/24/flattening-xml-data-in-sql-server.aspx
Using a query like this:
SELECT
a1.value('(RESPONSEID/text())[1]', 'varchar(50)') as RESPONSEID,
a2.value('(RESPONSESET/text())[1]', 'varchar(50)') as RESPONSESET,
a3.value('(NAME/text())[1]', 'varchar(50)') as NAME
FROM XmlSourceTable
CROSS APPLY XmlData.nodes('//Response') AS RESPONSEID(a1)
CROSS APPLY XmlData.nodes('//Response') AS RESPONSESET(a2)
CROSS APPLY XmlData.nodes('//Response') AS NAME(a3)
I got this to work once, but the shredded output was repeating values and not appearing in the table form I want which is like output below, though note in reality the table is very wide, at least 450 rows in all. Another issue is due to the width being greater than 255 I can't convert this to .txt and import it though I'd strongly prefer to consume and shred the native XML so this process can be automated:
RESPONSEID RESPONSESET NAME EXTERNALDATAREFERENCE EMAILADDRESS IPADDRESS STATUS STARTDATE ENDDATE
R_a4yThVvKXzVyftz Default Response Set Doe, John 1/1/2014 doej#gmail.com 123.12.123 0 9/18/2014 9:21 9/23/2014 16:09
R_06znwEis73yLsnX NonDefault Response Set Doe, Jane 1/1/2014 doeja#gmail.com 123.12.123 0 9/18/2014 5:29 9/29/2014 9:42
R_50HuB0jDFfI6hmZ Response Set 1 Doe, Cindy 1/1/2014 doec#gmail.com 123.12.123 0 9/18/2014 17:21 10/1/2014 11:45
I did find this application
https://www.novixys.com/ExultSQLServer/
to shred XML files which created a single table for the Nodehowever in addition to the response table it creates a table for each and every response node which results in about 500 additional tables. Also the application costs $250..
https://www.novixys.com/ExultSQLServer/
You don't need to add a cross apply for each value you want to extract. One is enough.
SELECT
R.X.value('(ResponseID/text())[1]', 'varchar(50)') as RESPONSEID,
R.X.value('(ResponseSet/text())[1]', 'varchar(50)') as RESPONSESET,
R.X.value('(Name/text())[1]', 'varchar(50)') as NAME
FROM XmlSourceTable
CROSS APPLY XmlData.nodes('//Response') AS R(X)

SQL Server XML Type Select Where Attribute = X From Any Tag

select *
from tablename
where CONVERT(xml, Sections).value('(/sections/section/#value)[1]', 'varchar(1)') = 'f'
will properly retrieve a record with the following value in the Sections column:
<sections><section value="f" priority="4" /><section value="a" priority="4" /></sections>
But misses this:
<sections><section value="w" priority="4" /><section value="f" priority="4" /></sections>
Obviously this is the problem "/sections/section/#value)[1]" but I don't understand the syntax and Google hasn't been too helpful. I found some code that got me this far, but I don't know how to modify it so that it will look through all tags instead of just the first one. I tried dropping the [1] but that gave the following error:
XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'
You can use exist().
select *
from tablename
where CONVERT(xml, Sections).exist('/sections/section[#value = "f"]') = 1
If you want to use some dynamic value instead a hard coded f in the query you can use sql:variable().
declare #Value varchar(10) = 'f'
select *
from tablename
where CONVERT(xml, Sections).exist('/sections/section[#value = sql:variable("#Value")]') = 1
If you have multiple entries of an XML tag, you need to use the .nodes() XQuery method:
select
*,
Sections(Section).value('(#value)[1]', 'varchar(1)')
from tablename
cross apply CONVERT(xml, Sections).nodes('/sections/section') AS Sections(Section)
With this, you create a "pseudo-table" called Sections(Section) that contains one XML row for each element that matches your XPath (for each <section> under <sections>). You can then reach into this pseudo-table and extract individual bits of information from those XML "rows" using hte usual .value() method

Resources