shred multiple xml elements into multiple columns - sql

I am trying to shred this xml column into multiple xml columns but it seems to be in the wrong format.
Here is my data from using xml path()
<claim id="1111111">
<InsHistDB>2</InsHistDB>
<ClaimHistID>111111</ClaimHistID>
<PatID>00000001</PatID>
<ProcedureData>
<row proc_logid="0000009" proc_logdb="1000000" createdate="2000-09-21T00:00:00" pldate="2000-09-21T00:00:00" adacode="D0120" />
<row proc_logid="1211557" proc_logdb="1000010" createdate="2015-09-21T00:00:00" pldate="2015-09-21T00:00:00" adacode="D0220" />
<row proc_logid="1211558" proc_logdb="1000010" createdate="2015-09-21T00:00:00" pldate="2015-09-21T00:00:00" adacode="D0230" />
<row proc_logid="1211556" proc_logdb="1000010" createdate="2015-09-21T00:00:00" pldate="2015-09-21T00:00:00" adacode="D0272" />
</ProcedureData>
</claim>
The select statement is currently this
SELECT TOP (1000) [ClaimID] as '#id'
,[InsHistDB]
,[ClaimHistID]
,[PatID]
-- ,[ProcedureData].value('declare namespace ns= "ProcedureData"; (/ns:ProcedureData/ns:row[1])','nvarchar(50)') as pp
,[ProcedureData]
--,[ProcedureData].query('proc_logid').value('.','varchar(50)') as 'proc_1'
FROM [Mine].[dbo].[claim]
where claimid=1111111
FOR XML PATH('claim')
What I am wanting to do is divide out the {Proc_Log_id} into different columns so the row should read.
Claim ID INSHISTDB CLaimHistID PATID Proc_Id1 Proc_ID2 ProcID3 procID4
11111 2 1111111 000000001 0000009 1211557 1211558 1211556
Is this possible or am i just spinning my wheels? Also this will be for multiple patid's so the query without the where clause is expected. Also I believe there can be up to 10 proc_logids per xml data point. I am fine with null values, as i plan to pivot and normalize this data.
Thanks for reading.

The syntax to select the 3rd node is column.value('(/foo/bar)[3]','varchar(25)').
You are right to consider other options. The 'query' or 'nodes' functions in particular are better suited as they could return the values as a second result set or a new XML document.

You can first graph the IDs from your XML content, and then you have to use PIVOT as you mentioned. Since you are not sure about number of columns, you can make use of dynamic pivot to make it more flexible.
First get the data using XML nodes and value and I stored the value in temp table as it would be convenient to do pivot.
Declare #xmlstring xml =
'<claim id="1111111">
<InsHistDB>2</InsHistDB>
<ClaimHistID>111111</ClaimHistID>
<PatID>00000001</PatID>
<ProcedureData>
<row proc_logid="0000009" proc_logdb="1000000" createdate="2000-09-21T00:00:00" pldate="2000-09-21T00:00:00" adacode="D0120" />
<row proc_logid="1211557" proc_logdb="1000010" createdate="2015-09-21T00:00:00" pldate="2015-09-21T00:00:00" adacode="D0220" />
<row proc_logid="1211558" proc_logdb="1000010" createdate="2015-09-21T00:00:00" pldate="2015-09-21T00:00:00" adacode="D0230" />
<row proc_logid="1211556" proc_logdb="1000010" createdate="2015-09-21T00:00:00" pldate="2015-09-21T00:00:00" adacode="D0272" />
</ProcedureData>
</claim>'
if object_id('tempdb..#temp1') is not null
drop table #temp1
select m.Col.value('#id','varchar(150)') as ClaimID
,m.Col.value('(InsHistDB)[1]','varchar(150)') as InsHistDB
,m.Col.value('(ClaimHistID)[1]','varchar(150)') as ClaimHistID
,m.Col.value('(PatID)[1]','varchar(150)') as PatID
,t.new.value('(#proc_logid)[1]', 'Varchar(150)') IDcol,
concat('Proc_ID', cast(ROW_NUMBER() over (Partition by m.Col.value('#id','varchar(150)') order by t.new.value('(#proc_logid)[1]', 'Varchar(150)')) as varchar(10))) AS ProcID
into #temp1
from #xmlstring.nodes('/claim') as m(col)
CROSS APPLY #xmlstring.nodes('claim/ProcedureData/row') as t(new);
You will have data in temp table like this. I have concatenated rownumber for a given claimID with proc ID to create column in the way you have mentioned.
ClaimID InsHistDB ClaimHistID PatID IDcol ProcID
1111111 2 111111 00000001 0000009 Proc_ID1
1111111 2 111111 00000001 1211557 Proc_ID2
1111111 2 111111 00000001 1211558 Proc_ID3
1111111 2 111111 00000001 1211556 Proc_ID4
Then you can make use of dynamic pivot to get your expected output.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.procID)
FROM #temp1 c
FOR XML PATH(''))
,1,1,'')
set #query = 'SELECT ClaimID, InsHistDB,ClaimHistID, PatID, '+#cols+' from
(
select ClaimID, InsHistDB,ClaimHistID, PatID, procID, IDcol
from #temp1
) x
pivot
(
max(IDcol)
for procID in (' + #cols + ')
) p '
Exec sp_executesql #query
Final output from the query:
ClaimID InsHistDB ClaimHistID PatID Proc_ID1 Proc_ID2 Proc_ID3 Proc_ID4
1111111 2 111111 00000001 0000009 1211557 1211558 1211556

Related

Parsing a xml string using sql

i am looking to parse the XML string using SQL .I like to have the data in separate columns.could some one please help?.
The string:
<item id="1" value="0"></item><item id="2" value="14"></item><item id="0" value="0"></item>
This is how you can do it in SQL Server (e.g., v2008):
create table #temp (xml_data xml)
insert into #temp values ('<item id="1" value="0"></item><item id="2" value="14"></item><item id="0" value="0"></item>')
select C.value('#id', 'int') as [id]
,C.value('#value', 'int') as [value]
from #temp cross apply
#temp.xml_data.nodes('item') as X(C)
drop table #temp
Which returns:
id value
----------- -----------
1 0
2 14
0 0

Shred & Concat XML into single column

what is simple way to produce output concatenated into single column for all Parameter values in sample below, this xml doesn't have an ns in it and I'm using SQL Server 2012.
I try to get this output, below snippet with xml sample produce 3 columns, and I'd like to make 1.
100 | Param1
,Param22
,Param3322
XML & code:
DECLARE #xml XML = '<Parameters>
<Parameter>
<Name>Param1</Name>
</Parameter>
<Parameter>
<Name>Param22</Name>
</Parameter>
<Parameter>
<Name>Param3322</Name>
</Parameter> </Parameters> '
SELECT 100 id
,#XML xmlinfo
INTO #t -- drop table #t -- select * from #t
DECLARE #xml XML = (
SELECT xmlinfo
FROM #t
)
SELECT (
SELECT ID
FROM #t
) AS ID
,X.STockData.query('Name[1]').value('.', 'Varchar(10)') AS 'Parameter'
---CONCAT(X.STockData.query('Name[1]').value('.','Varchar(10)'),X.STockData.query('Name[2]').value('.','Varchar(10)')) AS 'Parameter'
FROM #xml.nodes('Parameters/Parameter') AS X(StockData)
You can use XQuery for loop to construct comma-separated value from XML field, for example :
SELECT
t.ID as ID
, CAST(t.xmlinfo.query('
for $p in Parameters/Parameter
return
if ($p is (Parameters/Parameter[last()])[1])
then string($p/Name[1])
else concat($p/Name[1], ", ")
')
AS VARCHAR(MAX)
) as Parameter
FROM MyTable as t
Sqlfiddle Demo
The XQuery above simply loop through Parameter elements, and return Name child element if current Parameter is the last Parameter, otherwise return Name concatenated with comma.
SELECT (
SELECT ID
FROM #t
) AS ID
,X.STockData.query('Name[1]').value('.', 'Varchar(10)')
+ ', ' + X.STockData.query('Name[2]').value('.', 'Varchar(10)')
+ ', ' + X.STockData.query('Name[3]').value('.', 'Varchar(10)')
as parameter
FROM #xml.nodes('Parameters/Parameter') AS X(StockData)
Try this

How to Generate xml from sql for below pattern

I'm writing one stored procedure, which I have to create a xml column from db.
µ = CHAR(181) this is value separator,
¶ = CHAR(182) this is row separator
This is the statement I wrote. I know its not well formed.
SELECT #xmlString= CAST('<root><Section> ID =' + REPLACE(REPLACE ('20211µ1¶20212µ2', CHAR(182),
'</Section><Section> ID ='),CHAR(181), ' Slno=') + '</Section></root>' AS XML)
This is the pattern which I need to display like this.
<root>
<sections id="20211" slno="1" ></sections>
<sections id="20215" slno="2" ></sections>
</root>
declare #s varchar(50) = '20211µ1¶20212µ2'
declare #xmlString xml
;with C as
(
select T.N.value('value[1]', 'int') as id,
T.N.value('value[2]', 'int') as slno
from (select cast('<item><value>'+replace(replace(#s, 'µ','</value><value>'), '¶','</value></item><item><value>')+'</value></item>' as xml)) as X(XMLCol)
cross apply X.XMLCol.nodes('item') as T(N)
)
select #xmlString =
(
select C.id as [#id] ,
C.slno as [#slno]
from C
for xml path('sections'), root('root'), type
)
select #xmlString
Result:
<root>
<sections id="20211" slno="1" />
<sections id="20212" slno="2" />
</root>

How to remove column name when selecting from a XMLTYPE column using FOR XML PATH

I have a table with a XMLTYPE column named 'InvoiceXML'.
The data in this column is XML in the form:
<Invoice CustomerNum="1234" >
<CustomDeliveryDetails />
</Invoice>
When I do a
SELECT ... FOR XML PATH(''), ROOT('Invoices')
I end up with:
<Invoices>
<InvoiceXML>
<Invoice CustomerNum="1234" >
<CustomDeliveryDetails />
</Invoice>
</InvoiceXML>
</Invoices>
How do I stop the column name InvoiceXML appearing in the output?
declare #T table (invoiceXML xml)
insert into #T values (
'<Invoice CustomerNum="1234" >
<CustomDeliveryDetails />
</Invoice>
')
insert into #T values (
'<Invoice CustomerNum="4321" >
<CustomDeliveryDetails />
</Invoice>
')
select (select T.invoiceXML)
from #T as T
for xml path(''), root('Invoices')
Edit 1 The subquery (select T.invoiceXML) has no column name so it is removed.
Try:
SELECT cast(cast(InvoiceXML as nvarchar(max)) + '' as XML)
FROM whatever
FOR XML PATH(''), ROOT('Invoices')
Try this:
SELECT InvoiceXML.query('//Invoice')
FROM <YOUR_TABLE>
FOR XML PATH('')
Try specifying a xpath query to invoice in the FPR XML PATH
e.g: `FOR XML PATH('//InvoiceXML')`

MS SQL 2005 Table to XML

I have a simple table in SQL Server 2005, I wish to convert this to XML (using the "FOR XML" clause). I'm having trouble getting my XML to look like the required output.
I've tried looking through various tutorials on the web, but I am struggling. Can someone help?
The table I have looks like this
TYPE,GROUP,VALUE
Books,Hardback,56
Books,Softcover,34
CDs,Singles,45
CDS,Multis,78
The output style I need is:
<data>
<variable name="TYPE">
<row>
<column>GROUP</column>
<column>VALUE</column>
</row>
<row>
<column>GROUP</column>
<column>VALUE</column>
</row>
</variable>
<variable name="TYPE">
<row>
<column>GROUP</column>
<column>VALUE</column>
</row>
<row>
<column>GROUP</column>
<column>VALUE</column>
</row>
</variable>
</data>
Edit:
As far as I can tell I require the multiple values. I'm generating XML for use with Xcelsius (Linking XML and Xcelsius) so have no control over in the formatting of the XML. I can generate the XML using ASP as per the linked tutorial, but I was hoping to get it straight from SQL Server.
Edit 2:
I was hoping for something elegant and tidy... but Godeke's example got the closest. Some fiddling with the SQL and I've come up with:
select
"type" as '#name',
"group" as 'row/column',
null as 'row/tmp',
"value" as 'row/column'
from tableName
for xml path('variable'), root('data')
Outputs almost in the exact way I wanted. The null/tmp line doesn't even output; it is just preventing the concatenation. Still the tag <variable name="TYPE"> repeats for each row, which I can't have.
As close as I can get is this:
select "type" as '#name', "group" as 'row/column1', "value" as 'row/column2'
from tableName
for xml path('variable'), root('data')
Naming two items the same ("column" and "column") isn't something I know how to do in one pass, but on the other hand it is an odd XML schema choice; normally elements have unique names if they contain distinct data. The obvious choice (name them both 'row/column') simply concatenates them in the output into one value.
Also note that each returned row will be a "variable" element distinct from the others. To get the nesting without redundant records will require a subquery:
select distinct "type" as '#name'
from Agent
for xml path('variable'), root('data')
was my first thought, but the distinct prevents nesting.
All this makes me think that to get the exact output you need you might have to use EXPLICIT mode. Perhaps my problem is for something like this I punt and use a DOMDocument in code :).
I prefer using for XML PATH, it provides a nicer way to control your elements etc.
See
But this is quite tricky
/*
create table #tablename
(
[type] varchar(20),
[group] varchar(20),
[value] varchar(20)
)
insert into #tablename select 'type1','group11','value111'
insert into #tablename select 'type1','group11','value112'
insert into #tablename select 'type1','group12','value121'
insert into #tablename select 'type1','group12','value122'
insert into #tablename select 'type2','group21','value211'
insert into #tablename select 'type2','group21','value212'
insert into #tablename select 'type2','group22','value221'
insert into #tablename select 'type2','group22','value222'
alter table #tablename add id uniqueidentifier
update #tablename set id = newid()
*/
select [type] as '#name',
(select
(select [column] from
(
select [group] as 'column', tbn1.type, tbn2.[group]
from #tablename tbn3 WHERE tbn3.type = tbn1.type and tbn2.[group] = tbn3.[group]
union
select [value], tbn1.type, tbn2.[group]
from #tablename tbn3 WHERE tbn3.type = tbn1.type and tbn2.[group] = tbn3.[group]
) as s
for xml path(''),type
)
from #tablename tbn2
where tbn2.type = tbn1.type
for xml path('row3'), type
)
from #tableName tbn1
GROUP BY [type]
for xml path('variable'), root('data')
gives you what you are asking for I, but elegant and tidy it is not.
The script below produces the desired format
<DATA>
<VARIABLE TYPE="Books">
<row TYPE="Books">
<GROUP>Hardback</GROUP>
<VALUE>56</VALUE>
</row>
<row TYPE="Books">
<GROUP>Softcover</GROUP>
<VALUE>34</VALUE>
</row>
</VARIABLE>
<VARIABLE TYPE="CDs">
<row TYPE="CDs">
<GROUP>Singles</GROUP>
<VALUE>45</VALUE>
</row>
<row TYPE="CDS">
<GROUP>Multis</GROUP>
<VALUE>78</VALUE>
</row>
</VARIABLE>
</DATA>
Invoke
DECLARE #tblItems table (
[TYPE] varchar(50)
,[GROUP] varchar(50)
,[VALUE] int
)
DECLARE #tblShredded table (
[TYPE] varchar(50)
,[XmlItem] xml
)
DECLARE #xmlGroupValueTuples xml
insert into #tblItems([TYPE],[GROUP],[VALUE]) values( 'Books','Hardback',56)
insert into #tblItems([TYPE],[GROUP],[VALUE]) values( 'Books','Softcover',34)
insert into #tblItems([TYPE],[GROUP],[VALUE]) values( 'CDs','Singles',45)
insert into #tblItems([TYPE],[GROUP],[VALUE]) values( 'CDS','Multis',78)
SET #xmlGroupValueTuples =
(
SELECT
"#TYPE" = [TYPE]
,[GROUP]
,[VALUE]
FROM #tblItems
FOR XML PATH('row'), root('Root')
)
INSERT #tblShredded([TYPE], XmlItem)
SELECT
[TYPE] = XmlItem.value('./row[1]/#TYPE', 'varchar(50)')
,XmlItem
FROM dbo.tvfShredGetOneColumnedTableOfXmlItems(#xmlGroupValueTuples)
SELECT
(
SELECT
VARIABLE =
(
SELECT
"#TYPE" = t.[TYPE]
,(
SELECT
tInner.XmlItem.query('./child::*')
FROM #tblShredded tInner
WHERE tInner.[TYPE] = t.[TYPE]
FOR XML PATH(''), ELEMENTS, type
)
FOR XML PATH('VARIABLE'),type
)
)
FROM #tblShredded t
GROUP BY
t.[TYPE]
FOR XML PATH(''), ROOT('DATA')
where
-- Example Inputs
/*
DECLARE #xmlListFormat xml
SET #xmlListFormat =
'
<XmlListRoot>
<Item>004421UB7</Item>
<Item>59020UH24</Item>
<Item>542514NA8</Item>
</XmlListRoot>
'
*/
-- =============================================
-- Author: 6eorge Jetson
-- Create date: 01/22/3003
-- Description: Shreds an input XML list conforming to the expected list schema
-- =============================================
CREATE FUNCTION [dbo].[tvfShredGetOneColumnedTableOfXmlItems] (#xmlListFormat xml)
RETURNS
#tblResults TABLE (XmlItem xml)
AS
BEGIN
INSERT #tblResults
SELECT
tblShredded.colXmlItem.query('.') as XmlItem
FROM
#xmlListFormat.nodes('/child::*/child::*') as tblShredded(colXmlItem)
RETURN
END