Here's a link to my previous question on this same block of code with a working shred example
Ok, I'm a C# ASP.NET dev following orders: The orders are to take a given dataset, shred the XML and return columns. I've argued that it's easier to do the shredding on the ASP.NET side where we already have access to things like deserializers, etc, and the entire complex of known types, but no, the boss says "shred it on the server, return a dataset, bind the dataset to the columns of the gridview" so for now, I'm doing what I was told. This is all to head off the folks who will come along and say "bad requirements".
Task at hand:
Current code that doesn't work:
And if we modify the previous post to include namespaces on the XML elements, we lose the functionality that the previous post has...
DECLARE #table1 AS TABLE (
ProductID VARCHAR(10)
, Name VARCHAR(20)
, Color VARCHAR(20)
, UserEntered VARCHAR(20)
, XmlField XML
)
INSERT INTO #table1 SELECT '12345','ball','red','john','<sizes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><size xmlns="http://example.com/ns" name="medium"><price>10</price></size><size xmlns="http://example.com/ns" name="large"><price>20</price></size></sizes>'
INSERT INTO #table1 SELECT '12346','ball','blue','adam','<sizes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><size xmlns="http://example.com/ns" name="medium"><price>12</price></size><size xmlns="http://example.com/ns" name="large"><price>25</price></size></sizes>'
INSERT INTO #table1 SELECT '12347','ring','red','john','<sizes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><size xmlns="http://example.com/ns" name="medium"><price>5</price></size><size xmlns="http://example.com/ns" name="large"><price>8</price></size></sizes>'
INSERT INTO #table1 SELECT '12348','ring','blue','adam','<sizes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><size xmlns="http://example.com/ns" name="medium"><price>8</price></size><size xmlns="http://example.com/ns" name="large"><price>10</price></size></sizes>'
INSERT INTO #table1 SELECT '23456','auto','black','ann','<auto xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><type xmlns="http://example.com/ns">car</type><wheels xmlns="http://example.com/ns">4</wheels><doors xmlns="http://example.com/ns">4</doors><cylinders xmlns="http://example.com/ns">3</cylinders></auto>'
INSERT INTO #table1 SELECT '23457','auto','black','ann','<auto xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><type xmlns="http://example.com/ns">truck</type><wheels xmlns="http://example.com/ns">4</wheels><doors xmlns="http://example.com/ns">2</doors><cylinders xmlns="http://example.com/ns">8</cylinders></auto><auto xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><type xmlns="http://example.com/ns">car</type><wheels xmlns="http://example.com/ns">4</wheels><doors xmlns="http://example.com/ns">4</doors><cylinders xmlns="http://example.com/ns">6</cylinders></auto>'
DECLARE #x XML
-- I think I'm supposed to use WITH XMLNAMESPACES(...) here but I don't know how
SELECT #x = (
SELECT
ProductID
, Name
, Color
, UserEntered
, XmlField.query('
for $vehicle in //auto
return <auto
type = "{$vehicle/type}"
wheels = "{$vehicle/wheels}"
doors = "{$vehicle/doors}"
cylinders = "{$vehicle/cylinders}"
/>')
FROM #table1 table1
WHERE Name = 'auto'
FOR XML AUTO
)
SELECT #x
SELECT
ProductID = T.Item.value('../#ProductID', 'varchar(10)')
, Name = T.Item.value('../#Name', 'varchar(20)')
, Color = T.Item.value('../#Color', 'varchar(20)')
, UserEntered = T.Item.value('../#UserEntered', 'varchar(20)')
, VType = T.Item.value('#type' , 'varchar(10)')
, Wheels = T.Item.value('#wheels', 'varchar(2)')
, Doors = T.Item.value('#doors', 'varchar(2)')
, Cylinders = T.Item.value('#cylinders', 'varchar(2)')
FROM #x.nodes('//table1/auto') AS T(Item)
If my previous post shows there's a much better way to do this, then I really need to revise this question as well, but on the off chance this coding-style is good, I can probably go ahead with this as-is.
DECLARE #x XML;
with xmlnamespaces ('http://www.w3.org/2001/XMLSchema-instance' as xsi
, 'http://www.w3.org/2001/XMLSchema' as xsd
, 'http://example.com/ns' as ns)
SELECT #x = (
SELECT
ProductID
, Name
, Color
, UserEntered
, XmlField.query('
for $vehicle in //auto
return <auto
type = "{$vehicle/ns:type}"
wheels = "{$vehicle/ns:wheels}"
doors = "{$vehicle/ns:doors}"
cylinders = "{$vehicle/ns:cylinders}"
/>')
FROM #table1 table1
WHERE Name = 'auto'
FOR XML AUTO
)
Related
I am trying to generate a table containing one row that contains the XML of each temporary table in a stored procedure.
The problem is when the table is empty the FOR XML PATH('RS'), root('OR_RS') returns null.
What I want is: when the table is empty, return the structure of this table
something like this :
<OR_RS>
<ContactCode></ContactCode>
<EmailPaper></EmailPaper>
<ShortEmail></ShortEmail>
<WebSite></WebSite>
<Providers></Providers>
</OR_RS>
I have tried to do this :
-- ,ISNULL( (SELECT * FROM #OR_RS FOR XML PATH('RS'), root('OR_RS')), (SELECT ISNULL(ContactCode,'') , ISNULL(EmailPaper,'') , ISNULL(ShortEmail,'') , ISNULL(WebSite,'') , ISNULL(Providers,'') FROM #OR_RS FOR XML RAW)) as OR_RS
but it returns always Null:
SELECT 1 as Id, (SELECT * FROM #OR_MK FOR XML PATH('MK'), root('OR_MK') ) as OR_MK
,(SELECT * FROM #OR_CA FOR XML PATH('CA'), root('OR_CA') ) as OR_CA
-- ,ISNULL( (SELECT * FROM #OR_RS FOR XML PATH('RS'), root('OR_RS')), (SELECT ISNULL(ContactCode,'') , ISNULL(EmailPaper,'') , ISNULL(ShortEmail,'') , ISNULL(WebSite,'') , ISNULL(Providers,'') FROM #OR_RS FOR XML AUTO)) as OR_RS
,(SELECT * FROM #OR_RS FOR XML PATH('RS'), root('OR_RS')) as OR_RS
,(SELECT * FROM #OR_DC FOR XML PATH('DC'), root('OR_DC') ) as OR_DC
,(SELECT * FROM #BENEFICIARY FOR XML PATH('BEN'), root('BENEFICIARY') ) as BENEFICIARY
,(SELECT * FROM #MK_REPORT for XML PATH('REP'), root('MK_REPORT')) as MK_REPORT
Whatever you try to do with this...
You might want to have a look at sp_describe_first_result_set, but, since this is a procedure, it will not be that easy to get its result into your query...
You can use a trick (a RIGHT JOIN) to enforce a resultset. And with ELEMENTS XSINIL you force the engine to include the column in any case. Otherwise XML takes missing elements as NULL, so your empty result would not be written into the result XML at all otherwise. Try this:
DECLARE #tbl TABLE(ID INT IDENTITY,SomeValue INT,SomeString VARCHAR(100));
SELECT *
FROM #tbl
RIGHT JOIN (SELECT 1 AS x) AS tbl ON 1=1
FOR XML RAW, ELEMENTS XSINIL
The result
<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ID xsi:nil="true" />
<SomeValue xsi:nil="true" />
<SomeString xsi:nil="true" />
<x>1</x>
</row>
If you want to get more details, you might add ,XMLDATA or ,XMLSCHEMA to generate full meta data.
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
My XML structure:
<Items>
<Item>
<guid>FC550573-7171-997F-752D-8D65590CBFD6</guid>
<Objects>
<Object>
<type>0</type>
<guid>E10D9DA9-2C8D-8024-2F07-DF21395811BF</guid>
</Object>
<Object>
<type>0</type>
<guid>D8338400-35C7-781E-A039-C0FDDF80714A</guid>
</Object>
</Objects>
</Item>
</Items>
When filling the Objects Table:
CREATE TABLE [dbo].[Objects](
[item_guid] [varchar](36) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[type] [int] NOT NULL,
[guid] [varchar](36) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
) ON [PRIMARY]
Using the Query:
INSERT INTO [dbname].[dbo].[Objects]
([item_guid]
,[type]
,[guid])
SELECT
X.source.query('../../guid').value('.','VARCHAR(36)') as item_guid,
X.source.query('type').value('.','INT') as type,
X.source.query('guid').value('.','VARCHAR(36)') as guid
FROM(
Select xmldata from XmlFiles where fullpath=#fp
) AS T(x)
CROSS APPLY x.nodes('Items/Item/Objects/Object') As X(source)
This line is making the query VERY slow:
X.source.query('../../guid').value('.','VARCHAR(36)') as item_guid
What is the proper approach here?
Using /text() to get the value is good for performance on untyped XML. It can also be bad to use the parent axis ../.. (as #marc_s suggested).
Here is a version with a extra cross apply and /text() to get the values.
Try this:
select T2.N.value('(guid/text())[1]', 'uniqueidentifier') as item_guid,
T3.N.value('(type/text())[1]', 'int') as type,
T3.N.value('(guid/text())[1]', 'uniqueidentifier') as guid
from (SELECT xmldata FROM dbo.XmlFiles WHERE fullpath = #fp) as T1(N)
cross apply T1.N.nodes('Items/Item') as T2(N)
cross apply T2.N.nodes('Objects/Object') as T3(N)
You have to be the judge which query is the fastest for you.
I just want to add, in case anybody else runs across this, that adding the following option makes a huge difference.
OPTION (OPTIMIZE FOR (#testXml = NULL))
If you'd like to test this yourself, here is a short test script I was running. Just look at the estimated subtree cost between these.
declare #testXml xml set #testXml = '<filters><filter name="test name" type="GREATERTHAN">1</filter><filter name="CLAIMID" type="GREATERTHAN">1</filter></filters>'
select x.value('#name','nvarchar(100) ') filtername,
x.value('.','nvarchar(200)')filtervalue,
x.value('#type','nvarchar(50) ') filtertype
from #testXml.nodes('/filters/filter') as ref(x)
--vs...
select x.value('#name','nvarchar(100) ') filtername,
x.value('.','nvarchar(200)')filtervalue,
x.value('#type','nvarchar(50) ') filtertype
from #testXml.nodes('/filters/filter') as ref(x)
OPTION (OPTIMIZE FOR (#testXml = NULL))
Try this,
We will create a temp table variable for store this xml values & insert to corresponding table Objects
//..Xml value to temp variable
Declare #x xml ='<Items><Item><guid>FC550573-7171-997F-752D-8D65590CBFD6</guid><Objects><Object>
<type>0</type><guid>E10D9DA9-2C8D-8024-2F07-DF21395811BF</guid></Object><Object>
<type>0</type><guid>D8338400-35C7-781E-A039-C0FDDF80714A</guid></Object></Objects>
</Item></Items>';
Declare #Temp_Tbl table (RowId int identity, item_guid nvarchar(36), [type] int, [guid] nvarchar(36));
Insert into #Temp_Tbl SELECT #x.value('(/Items/Item/guid)[1]', 'nvarchar(36)'),
Cont.value('(type)[1]', 'int'), Cont.value('(guid)[1]', 'nvarchar(36)')
FROM #x.nodes('/Items/Item/Objects/Object') AS Obj(Cont);
INSERT INTO [dbo].[Objects] Select item_guid,[type],[guid] from #Temp_Tbl;
I have a query in a stored procedure retrieving some data in XML format to be returned in a variable #xml_data, like this:
SELECT #xml_data = (
SELECT * FROM (
SELECT 1 AS Tag
,0 AS Parent
.....
FROM MyTable
WHERE id = #id
UNION ALL
SELECT 2 AS Tag
,1 AS Parent
....
FROM MyTable2
WHERE id = #id
UNION ALL
SELECT 3 AS Tag
,2 AS Parent
....
FROM MyTable3
WHERE id = #id
) results
FOR XML EXPLICIT, TYPE)
This is working like the proverbial dream :)
However, I'd like to concatenate a header to this XML (e.g. <xml version="1.0" encoding="ISO-8859-1"/>) and can't figure out how to do it. I've tried converting to NVARCHAR, selecting the two variables in one statement but can't seem to get it right.
Can anyone help??
Thanks :)
Try doing like this:
DECLARE #x xml
DECLARE #strXML varchar(MAX)
SET #x=N'<Employee><Name>Luftwaffe</Name></Employee>'
set #strXML = '<xml version="1.0" encoding="ISO-8859-1"/>' + CONVERT(varchar(MAX),#x)
SELECT #strXML
Hope it helps !
You can just declare the string at the beginning and concatenate them together:
declare #xml_data nvarchar(MAX)
set #xml_data = '<xml version="1.0" encoding="ISO-8859-1"/>'
SELECT #xml_data = #xml_data + (
SELECT * FROM (
SELECT 1 AS Tag
,0 AS Parent
.....
FROM MyTable
WHERE id = #id
UNION ALL
SELECT 2 AS Tag
,1 AS Parent
....
FROM MyTable2
WHERE id = #id
UNION ALL
SELECT 3 AS Tag
,2 AS Parent
....
FROM MyTable3
WHERE id = #id
) results
FOR XML EXPLICIT, TYPE)
This is the easiest way, in my opinion:
declare #xml1 xml
declare #xml2 xml
declare #xml3 xml
select #xml1='<name>testname</name>'
select #xml2='<value>testvalue</value>'
select #xml3 =
(
select #xml1 AS xml1, #xml2 AS xml2
for xml path('')
)
select #xml3
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.