how to generate xml with element and attribute using xml explicit - sql

im trying to generate xml in the following format:
<Root>
<Domain>Abc</Domain>
<Name>Xyz</Name>
<Contents>
<Content>
<ID>1</ID>
<Value>Test 1</Value>
<Record ID="1">Test 1</Record>
</Content>
<Content>
<ID>2</ID>
<Value>Test 2</Value>
<Record ID="2">Test 2</Record>
</Content>
</Contents>
</Root>
My query is as follows:
declare #TestTable table (ID int, Value varchar(100))
insert into #TestTable values (1,'Test 1')
insert into #TestTable values (2,'Test 2')
declare #Domain varchar(max)='Abc'
declare #Name varchar(max)='Xyz'
SELECT
1 AS Tag,
NULL AS Parent,
#Domain as 'Root!1!Domain!Element',
#Name as 'Root!1!Name!Element',
NULL as 'Contents!2!Element',
NULL as 'Content!3!ID!Element',
NULL as 'Content!3!Value!Element',
NULL as 'Content!3!Record!Element'
union
SELECT
2 AS Tag,
1 AS Parent,
NULL,NULL,NULL,NULL,NULL,NULL
union
select
3 as Tag,
2 as Parent,
NUll,NUll,NULL,
ID,Value,Value
from #TestTable
FOR XML EXPLICIT
my query does not produce the record tag completely, currently it is
<Record>Test 2</Record>
which should be as
<Record ID=2>Test 2</Record>
I tried all the possibilities but not getting the tag. Can anyone help me solving this issue.

I could not get the expected output from xml explicit, instead i used xml path and got the output. this is my updated query
SELECT
#Domain "Domain",
#Name "Name",
(
SELECT
ID "ID",
Value "Value",
(select
ID "#ID",
Value as "text()"
FOR XML PATH('Record'), ELEMENTS, TYPE )
FROM #TestTable
FOR XML PATH ('Content'), TYPE, ROOT('Contents')
)
FOR XML PATH ('Root')
you are welcome to post the fix which uses xml explicit.

Don't forget the ORDER BY when use FOR XML EXPLICIT.
SELECT
1 AS Tag,
NULL AS Parent,
#Domain as 'Root!1!Domain!Element',
#Name as 'Root!1!Name!Element',
NULL as 'Contents!2!Element',
NULL as 'Content!3!ID!Element',
NULL as 'Content!3!Value!Element',
NULL as 'Record!4!ID',
NULL as 'Record!4!'
union
SELECT
2 AS Tag,
1 AS Parent,
#Domain,NULL,NULL,NULL,NULL,NULL, null
union
select
3 as Tag,
2 as Parent,
#Domain,NUll,NULL,
ID,Value,null,null
from #TestTable
union
select
4 as Tag,
3 as Parent,
#Domain,NUll,NULL,
ID,Value,ID,Value
from #TestTable
ORDER BY 'Root!1!Domain!Element','Contents!2!Element','Content!3!ID!Element','Record!4!ID'
FOR XML EXPLICIT

Related

xml.exist - sql:column to sequence

I try to create a query with xml.exist by using the sql:column-function where the value of the column should be transformed into a usable XQuery-sequence.
This is the query with a static sequence, which is working.
SELECT
FieldA
, FieldB
FROM
MyTable
WHERE
FieldC.exist('DSAuth/role[#id=("195", "267", "350")')
To get a dynamic sequence i would use a table function with returns a table with a column "IDsequence" that have all id's as a string. e.g. ' "195", "267", "350" '.
The table function should return only one row! With multiple rows it works, but i have to group the result at last, which is bad for performance.
SELECT
FieldA
, FieldB
FROM
MyTable
CROSS APPLY
dbo.MyFunc(0) AS f
WHERE
FieldC.exist('DSAuth/role[#id=sql:column("f.IDsequence")]')
Is there a way to get a usable sequence for XQuery from sql:column("f.IDsequence")?
Thanks for help.
Edit:
The problem in performance is that FieldB (and a few more fields) is a xml column, so i have to convert it to group by.
SELECT
FieldA
, CAST(CAST(FieldB AS nvarchar(max)) AS xml) AS FieldB
FROM
MyTable
CROSS APPLY
dbo.MyFunc(0) AS f
WHERE
FieldC.exist('DSAuth/role[#id=sql:column("f.IDsequence")]')
GROUP BY
FieldA
, CAST(FieldB AS nvarchar(max))
I don't know if this is the easiest approach, but you might try to include your list into the XML and use it in the predicate like here:
--A mockup-table to show the principles
DECLARE #tbl TABLE(TheXml XML)
INSERT INTO #tbl VALUES
(
N'<root>
<a>1</a>
<a>2</a>
</root>'
)
,(
N'<root>
<a>1</a>
</root>'
)
,(
N'<root>
<a>3</a>
<a>4</a>
</root>'
);
--This is easy: Just one value
SELECT * FROM #tbl WHERE TheXml.exist('/root/a[. cast as xs:int?=1]')=1;
--This is your sequence.
--But you cannot introduce the value list with sql:column() or sql:variable().
SELECT * FROM #tbl WHERE TheXml.exist('/root/a[. cast as xs:int?=(2,3)]')=1;
--But you can add the values to your XML before like in this cte
DECLARE #values TABLE(val INT);
INSERT INTO #values VALUES(2),(3);
WITH cte(NewXml) AS
(
SELECT (SELECT (SELECT val AS [#val] FROM #values FOR XML PATH('values'),TYPE)
,TheXml AS [*]
FOR XML PATH(''),TYPE
)
FROM #tbl t
)
SELECT NewXml.query('/root') TheXml
FROM cte
WHERE NewXml.exist('/root/a[. cast as xs:int?=/values/#val]')=1;
The intermediate XML looks like this:
<values val="2" />
<values val="3" />
<root>
<a>1</a>
<a>2</a>
</root>
The final .query('/root') will return the previous XML unchanged.
UPDATE
The same would work with an introduction on string base like here:
WITH cte(NewXml) AS
(
SELECT (SELECT CAST(N'<values val="2" /><values val="3" />' AS XML)
,TheXml AS [*]
FOR XML PATH(''),TYPE
)
FROM #tbl t
)
SELECT NewXml.query('/root') TheXml
FROM cte
WHERE NewXml.exist('/root/a[. cast as xs:int?=/values/#val]')=1

Incorrect structure of XML generated in SQL

Iv had another problem, i just change my UNION to UNION ALL and it work correctly but i want to add another XML path (at the beginning) - this will be a const for both querys.
(SELECT 1 AS "ns0:kindOfItem",
code AS "ns0:wholeCode",
REPLACE(weight, ',', '.') AS "ns0:weight",
1 AS "ns0:ammountOfNumbers",
(SELECT price AS "ns0:value",
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:sendedItems'), TYPE),
(SELECT
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:present'), TYPE)
FROM [PL].[dbo].[dk_documents] where id in (1,2,3)
UNION ALL
(SELECT 1 AS "ns0:kindOfItem",
code AS "ns0:wholeCode",
REPLACE(weight, ',', '.') AS "ns0:weight",
1 AS "ns0:ammountOfNumbers",
(SELECT price AS "ns0:value",
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:sendedItems'), TYPE),
(SELECT
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:present'), TYPE)
FROM [PL2].[dbo].[dk_documents] where id in (1,2,3)
FOR XML PATH('test'))
It work correctly but i want sth like this :
SELECT 1 as test,
(SELECT 1 AS "ns0:kindOfItem",
code AS "ns0:wholeCode",
REPLACE(weight, ',', '.') AS "ns0:weight",
1 AS "ns0:ammountOfNumbers",
(SELECT price AS "ns0:value",
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:sendedItems'), TYPE),
(SELECT
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:present'), TYPE)
FROM [PL].[dbo].[dk_documents] where id in (1,2,3)
UNION ALL
(SELECT 1 AS "ns0:kindOfItem",
code AS "ns0:wholeCode",
REPLACE(weight, ',', '.') AS "ns0:weight",
1 AS "ns0:ammountOfNumbers",
(SELECT price AS "ns0:value",
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:sendedItems'), TYPE),
(SELECT
'EUR' as "ns0:currency"
FOR XML PATH ('ns0:present'), TYPE)
FROM [PL2].[dbo].[dk_documents] where id in (1,2,3)
FOR XML PATH('test'))
FOR XML PATH('anotherPath')
i got this error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
Output should be like
<test>1</test>
<>All of tese columns from QUERY with union ALL</>
Just an example :
WITH XMLNAMESPACES(DEFAULT 'Dummy')
SELECT 1 as test,
2 as anotherOne,
(SELECT * FROM
(SELECT id, symbol from table1
WHERE id in (1,2,3)
UNION ALL
SELECT id, nrdok from table2
WHERE id in (4,5,6))as yolo
FOR XML PATH(''),TYPE)
FOR XML PATH('test')
It gives me an output :
<test xmlns="Dummy">
<test>1</test>
<anotherOne>2</anotherOne>
<id xmlns="Dummy">1</id>
<symbol xmlns="Dummy">test10</symbol>
<id xmlns="Dummy">2</id>
<symbol xmlns="Dummy">test10</symbol>
<id xmlns="Dummy">3</id>
<symbol xmlns="Dummy">test10</symbol>
<id xmlns="Dummy">4</id>
<symbol xmlns="Dummy">test11</symbol>
<id xmlns="Dummy">5</id>
<symbol xmlns="Dummy">test11</symbol>
<id xmlns="Dummy">6</id>
<symbol xmlns="Dummy">test11</symbol>
</test>
And i want :
<test xmlns="Dummy">
<test>1</test>
<anotherOne>2</anotherOne>
<id>1</id>
<symbol>test10</symbol>
<id>2</id>
<symbol>test10</symbol>
<id>3</id>
<symbol>test10</symbol>
<id>4</id>
<symbol>test11</symbol>
<id>5</id>
<symbol>test11</symbol>
<id>6</id>
<symbol>test11</symbol>
</test>
As pointed out at your previous question the repeated namespaces are not wrong, just annoying, and - if there are many and long URIs, they can blow up your XML to remarkable size...
There I placed a link to a related question already. The trick is to create the XML without the namespace and add the namespace in the finaly SELECT ... FOR XML PATH only:
But I must admit, that after a long while of trial and error I found, that there seem to be a bug if the DEFAULT namespace is involved. Any approach I tried led to either repeated namespace declarations or to repeated empty namespace declarations.
So the only solution I could find is this (I go to wash my fingers now :-) ):
DECLARE #table1 TABLE(id INT,symbol VARCHAR(100));
INSERT INTO #table1 VALUES
(1,'Test 1')
,(2,'Test 2')
,(3,'Test 3')
,(4,'Test 4');
DECLARE #nordic_table2 TABLE(id INT,nrdok VARCHAR(100));
INSERT INTO #nordic_table2 VALUES
(1,'Test 1')
,(2,'Test 2')
,(3,'Test 3')
,(4,'Test 4');
DECLARE #XmlWithoutNamespace XML=
(
SELECT 1 as test
,2 as anotherOne
,(
SELECT *
FROM
(
SELECT id, symbol from #table1
WHERE id in (1,2,3)
UNION ALL
SELECT id, nrdok from #nordic_table2
WHERE id in (4,5,6)
) AS yolo
FOR XML PATH(''),TYPE
)
FOR XML PATH('')
);
SELECT
CAST(
'<test xmlns="Dummy">'
+
CAST(#XmlWithoutNamespace AS NVARCHAR(MAX))
+
'</test>'
AS XML);
UPDATE
I strongly advise you to change your structure to this
SELECT id AS [#id]
,symbol AS [*]
FROM
(
SELECT id, symbol from #table1
WHERE id in (1,2,3)
UNION ALL
SELECT id, nrdok from #nordic_table2
WHERE id in (4,5,6)
) AS yolo
FOR XML PATH('symbol'),TYPE
The result would be this, which is much better to read and to query...
<test xmlns="Dummy">
<test>1</test>
<anotherOne>2</anotherOne>
<symbol id="1">Test 1</symbol>
<symbol id="2">Test 2</symbol>
<symbol id="3">Test 3</symbol>
<symbol id="4">Test 4</symbol>
</test>
UPDATE 2: More Namespaces...
It is actually really hard - almost impossible - to deal with namespaces properly. There is some highly developed logic within FOR XML PATH and in methods like .modify(). I tried several approaches but did not find a convincing one...
The only way I found is very ugly. The trick is, to create 1-level-XML only (no nested elements from sub-selects!) and store them as strings. But before you cut away the root with the namespace declarations. Doing so you'll get invalid XML fragments.
You concatenate them and CAST the whole lot in the last step back to XML.
--Just a container to collect the XML parts
DECLARE #CollectXML TABLE(ID INT IDENTITY,Content XML,CleanedAsString NVARCHAR(MAX));
--The final ",ROOT('xyz')" will force the namespace's declaration into the root node
WITH XMLNAMESPACES('SomeTestUri' AS abc)
INSERT INTO #CollectXML(Content)
SELECT
(
SELECT 1 as [abc:test]
,2 as anotherOne
FOR XML PATH(''),ROOT('xyz'),TYPE
);
--No we use ugly string manipulation to cut out the inner part without the namespace declaration
UPDATE #CollectXML SET CleanedAsString=
(
SELECT Rest
FROM #CollectXML
CROSS APPLY (SELECT CAST(Content AS NVARCHAR(MAX))) AS Casted(XmlAsString)
CROSS APPLY (SELECT REVERSE(SUBSTRING(XmlAsString,CHARINDEX('>',XmlAsString)+1,LEN(XmlAsString)))) AS Cut1(part1Reverse)
CROSS APPLY (SELECT REVERSE(SUBSTRING(part1Reverse,CHARINDEX('<',part1Reverse)+1,LEN(part1Reverse)))) AS Cut2(Rest)
WHERE ID=1
)
WHERE ID=1;
--The same with the second part
WITH XMLNAMESPACES('SomeTestUri' AS abc)
INSERT INTO #CollectXML(Content)
SELECT
(
SELECT id AS [#abc:id]
,symbol AS [*]
FROM
(
SELECT id, symbol from #table1
WHERE id in (1,2,3)
UNION ALL
SELECT id, nrdok from #nordic_table2
WHERE id in (4,5,6)
) AS yolo
FOR XML PATH('abc:symbol'),ROOT('xyz'),TYPE --the not needed root will take the namespace declaration out of the deeper elements
);
--and the ugly string manipulation
UPDATE #CollectXML SET CleanedAsString=
(
SELECT Rest
FROM #CollectXML
CROSS APPLY (SELECT CAST(Content AS NVARCHAR(MAX))) AS Casted(XmlAsString)
CROSS APPLY (SELECT REVERSE(SUBSTRING(XmlAsString,CHARINDEX('>',XmlAsString)+1,LEN(XmlAsString)))) AS Cut1(part1Reverse)
CROSS APPLY (SELECT REVERSE(SUBSTRING(part1Reverse,CHARINDEX('<',part1Reverse)+1,LEN(part1Reverse)))) AS Cut2(Rest)
WHERE ID=2
)
WHERE ID=2;
--The XML is put together and - as the very last step! - casted back to XML
SELECT
CAST(
'<test xmlns="Dummy" xmlns:abc="SomeTestUri">'
+
(SELECT CleanedAsString FROM #CollectXML WHERE ID=1)
+
(SELECT CleanedAsString FROM #CollectXML WHERE ID=2)
+
'</test>'
AS XML);
The result for this
<test xmlns="Dummy" xmlns:abc="SomeTestUri">
<abc:test>1</abc:test>
<anotherOne>2</anotherOne>
<abc:symbol abc:id="1">Test 1</abc:symbol>
<abc:symbol abc:id="2">Test 2</abc:symbol>
<abc:symbol abc:id="3">Test 3</abc:symbol>
<abc:symbol abc:id="4">Test 4</abc:symbol>
</test>
You can test below SQL Select statement.
I hope it helps,
declare #xml xml
declare #xml1 xml
set #xml1 = '<test>1</test>'
declare #xml2 xml
set #xml2 = ( select top 4 name from sys.databases FOR XML PATH('database') )
set #xml = (SELECT #xml1, #xml2 FOR XML PATH(''))
select #xml
Output will be as follows

How to Insert Different XML Field values as rows in a table

I Have an xml as mentioned below,
declare #Message as xml
set #Message='<message>
<body>
<ID>1</ID>
<setup_time>10</setup_time>
<prod_cycle_time>10</prod_cycle_time>
<unit>cas</unit>
<Flag>NULL</Flag>
<FillingPO>NULL</FillingPO>
<PackAtCAN1>NULL</PackAtCAN1>
</body>
</message>'
from above xml structure need to insert few fields data as mentioned below format
ID Desc Value
1 setup_time 10
1 prod_cycle_time 10
1 unit Case
1 Flag NULL
You can also use XQuery to first transform your XML:
declare #transform xml
set #transform = #message.query('
let $capture := ("setup_time","prod_cycle_time","unit","Flag")
for $item in /message/body/*
let $id := $item/../ID
where $capture = local-name($item)
return <Row>
<ID>{$id/text()}</ID>
<Desc>{local-name($item)}</Desc>
<Value>{$item/text()}</Value>
</Row>
')
SELECT x.value('ID[1]','int') AS ID,
x.value('Desc[1]','varchar(max)') AS [Desc],
x.value('Value[1]','varchar(max)') AS Value
FROM #transform.nodes('/Row') tmp(x)
Looks like you want to get the values out from the XML and then do an unpivot.
Use value() function to extract values from the XML and you can use union all to do the unpivot.
declare #Message as xml
set #Message='
<message>
<body>
<ID>1</ID>
<setup_time>10</setup_time>
<prod_cycle_time>10</prod_cycle_time>
<unit>cas</unit>
<Flag>NULL</Flag>
<FillingPO>NULL</FillingPO>
<PackAtCAN1>NULL</PackAtCAN1>
</body>
</message>'
declare #ID int
set #ID = #Message.value('(/message/body/ID/text())[1]', 'int')
select #ID as ID,
'setup_time' as [Desc],
#Message.value('(/message/body/setup_time/text())[1]', 'nvarchar(50)') as Value
union all
select #ID,
'prod_cycle_time',
#Message.value('(/message/body/prod_cycle_time/text())[1]', 'nvarchar(50)')
union all
select #ID,
'unit',
#Message.value('(/message/body/unit/text())[1]', 'nvarchar(50)')
union all
select #ID,
'Flag',
#Message.value('(/message/body/Flag/text())[1]', 'nvarchar(50)')

Concatenate multiple xml columntype rows in SQL

I have an online form, whereby each entry adds the data as xml to an xml column in SQL.
ApplicationID(uniqueidentifier) | DateModified | ApplicationForm(XML)
What I need to do is perform a select query that will grab all the ApplicationForm xml values, and concatenate them together to form one result, e.g.
Row1: <ApplicationForm type=""></ApplicationForm>
Row2: <ApplicationForm type=""></ApplicationForm>
Select result:
<Applications>
<ApplicationForm type=""></ApplicationForm>
<ApplicationForm type=""></ApplicationForm>
</Applications>
Probably a better way to do it than this, but I specified the ApplicationForm tag as a tag to be removed, and then stripped it out.
DECLARE #a TABLE (ID UNIQUEIDENTIFIER,
DateModified DATETIME,
ApplicationForm XML)
INSERT INTO #a
( ID, DateModified, ApplicationForm )
VALUES ( NEWID(), -- ID - uniqueidentifier
'2010-12-07 18:47:36', -- DateModified - datetime
'<Application><Form>123</Form></Application>'
) ,
( NEWID(), -- ID - uniqueidentifier
'2010-12-07 18:47:36', -- DateModified - datetime
'<Application><Form>456</Form></Application>'
)
DECLARE #Result VARCHAR(MAX)
SET #Result = CONVERT(VARCHAR(MAX), ( SELECT ApplicationForm AS "StripTagOut"
FROM #a
FOR XML PATH(''), ROOT('Applications'), TYPE ))
SELECT CONVERT(xml, REPLACE(REPLACE(#Result, '</StripTagOut>', ''), '<StripTagOut>', ''))
This was an experiment in using XML EXPLICIT which I believe works:
SELECT 1 AS Tag,
NULL AS Parent,
NULL [Applications!1],
NULL [ApplicationForm!2!!XMLTEXT]
UNION ALL
SELECT 2 AS Tag,
1 AS Parent,
NULL [Applications!1],
ApplicationForm [ApplicationForm!2!!XMLTEXT]
FROM YourTable
FOR XML EXPLICIT
For my own gratification, here is the sample script I used
CREATE TABLE XmlTest
(
ApplicationForm xml
)
INSERT INTO XmlTest VALUES ('<ApplicationForm type="a"><SomeTag>SomeContent</SomeTag></ApplicationForm>')
INSERT INTO XmlTest VALUES ('<ApplicationForm type="b"><SomeTag>SomeOtherContent</SomeTag></ApplicationForm>')
SELECT 1 AS Tag,
NULL AS Parent,
NULL [Applications!1],
NULL [ApplicationForm!2!!XMLTEXT]
UNION ALL
SELECT 2 AS Tag,
1 AS Parent,
NULL [Applications!1],
ApplicationForm [ApplicationForm!2!!XMLTEXT]
FROM XmlTest
FOR XML EXPLICIT
Which output
<Applications>
<ApplicationForm type="a">
<SomeTag>SomeContent</SomeTag>
</ApplicationForm>
<ApplicationForm type="b">
<SomeTag>SomeOtherContent</SomeTag>
</ApplicationForm>
</Applications>
Take a look at this page, which lists quite a number of ways to concatenate rows of data.

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