I would like to query multiple tables from SQL Server 2005 and create a single XML document and do this in a stored procedure.
I know that I can query multiple tables within a stored procedure and get a DataSet in my .NET application that can be easily saved as XML. However, I'm trying to do something similar within the context of a stored procedure.
Essentially I want to do something like this:
declare #x xml
select #x = x.result
from (select y.* from tabley y for xml path('y')
union
select a.* from tablea a for xml path('aa')
) as x
select #x
If you want them just one after the other, you can try something like this:
SELECT
(SELECT y.* FROM dbo.TableY FOR XML PATH('y'), TYPE) AS 'YElements',
(SELECT a.* FROM dbo.TableA FOR XML PATH('aa'), TYPE) AS 'AElements'
FOR XML PATH(''), ROOT('root')
That return an XML something like:
<root>
<YElements>
<Y>
....
</Y>
<Y>
....
</Y>
......
</YElements>
<AElements>
<A>
....
</A>
<A>
....
</A>
......
</AElements>
</root>
SELECT -- Root Starts
(SELECT '1' AS ErrorCode FOR XML PATH(''), TYPE) AS 'Results', -- Level 1 Starts
(select -- Level 2 Starts
(select '1' CustomerID, -- Level 2 Detail Starts
'John' CustomerName,
'Doe' CustomerLastname,
'Y' Active
for xml path('Customers'), type) AS 'Customer' -- Level 2 Detail Ends
for xml path(''), TYPE) AS 'Response' -- Level 2 Ends
FOR XML PATH('') -- Level 1 Ends
,ROOT('BaseXML') -- Root Ends
Convert Table XML in sql server.
Declare #RESULTXML XML
Declare #SMS_REGISTER TABLE([id] VARCHAR(30),[status] VARCHAR(30))
Declare #EMAIL_REGISTER TABLE([id] VARCHAR(30),[status] VARCHAR(30))
Declare #ODP_REGISTER TABLE([id] VARCHAR(30),[status] VARCHAR(30))
Select #RESULTXML =(
SELECT (SELECT * FROM #SMS_REGISTER FOR XML PATH('sms'), TYPE) AS 'smss',
(SELECT * FROM #EMAIL_REGISTER FOR XML PATH('email'), TYPE) AS 'emails',
(SELECT * FROM #ODP_REGISTER FOR XML PATH('odp'), TYPE) AS 'odps'
FOR XML PATH('subroot'), ROOT('root') )
Return XML Like this
<root>
<subroot>
<smss>
<sms>
<id>NT0000000020</id>
<status>registered</status>
</sms>
<sms>
<id>NT0000000021</id>
<status>registered</status>
</sms>
<sms>
<id>NT0000000022</id>
<status>registered</status>
</sms>
<sms>
<id>NT0000000023</id>
<status>registered</status>
</sms>
</smss>
<emails>
<email>
<id>NT0000000024</id>
<status>registered</status>
</email>
<email>
<id>NT0000000025</id>
<status>registered</status>
</email>
</emails>
</subroot>
</root>
Related
Lets say I have table T with one column A.
What I would like to achieve as result is xml like this :
<not>
<mes>not important what include</mes>
<A>1</A>
<A>2</A>
<A>3</A>
<A>4</A>
...
</not>
Was trying something similar to :
SELECT
'important' AS [mes],
(select A as [A] from T)- of course that part is incorrect but don't know how to handle it
FROM T2
FOR XML PATH ('not');
Please advice.
Updated QUERY
SET #SQL = '
WITH XMLNAMESPACES (''https://something..'' as ns)
SELECT
Q.DocumentType AS [#type],
Q.ReferenceNo AS [#ref],
Q.Id AS [#id],
D.DocId AS [#docId],
N.NotId AS [#notId],
CONVERT(char(10), N.CreationDate, 126) AS [#notdate],
''mes'' AS [mes/#content],
#mes2 AS [mes/content],
[mes] = ''important'' ,
(select A from '+ Cast(#TableName as VARCHAR(60))+' FOR XML PATH (''''), type)
FROM
[DB].[dbo].[tab1] AS Q
LEFT JOIN [DB].[dbo].[tab2] AS D ON Q.ID=D.ID
LEFT JOIN [DB].[dbo].[tab3] AS N ON D.ID=N.DocId
WHERE
Q.ID='+ Cast(#Id as varchar(15))+'
FOR XML PATH (''not'')';
execute (#SQL);
Perhaps this will help
-- Just a DEMONSTRATIVE Table Variable
--------------------------------------------
Declare #YourTable Table ([A] varchar(50))
Insert Into #YourTable Values
(1)
,(2)
,(3)
,(4)
SELECT [mes] = 'important'
,( Select A from #YourTable For XML Path(''),type )
FOR XML PATH ('not');
Results
<not>
<mes>important</mes>
<A>1</A>
<A>2</A>
<A>3</A>
<A>4</A>
</not>
I hate dealing with XML queries. I don't do it often enough to remember how to format everything, but I'm at my wits' end.
Let's say I have a table called Messages and a column inside it called Payload. Payload contains XML stored as varchar(max). The XML is formatted as such:
<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
</NCOAPACP>
How do I query the table to retrieve a list of the values in the VID node?
Try this:
declare #w as xml = cast('
<NCOAPACP xmlns:i=''http://www.w3.org/2001/XMLSchema-instance''
xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
</NCOAPACP>' as xml)
SELECT #w.value('declare namespace ns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM";
ns:NCOAPACP[1]/ns:VID[1]','varchar(10)') AS VID
If you have multiple VID node you can try this:
declare #w as xml = cast('
<NCOAPACP xmlns:i=''http://www.w3.org/2001/XMLSchema-instance''
xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
<VID>454545</VID>
</NCOAPACP>' as xml)
SELECT VID.value('.','varchar(10)')
from #w.nodes(
'declare namespace ns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM";
ns:NCOAPACP[1]/ns:VID'
)
AS C(VID)
If XML data are in a table column then the solution is a little more tricky. Especially if data type is varchar(max).
declare #tbl table(id int,payload varchar(max))--sample table
--sample data
insert #tbl values
(1,'<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
<VID>3656184</VID>
</NCOAPACP>'),
(2,'<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>123456</VID>
<VID>987654</VID>
</NCOAPACP>')
--it is convenient to define namespaces before the query
;with xmlnamespaces('uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM' as x)--we will use this x:
select id, t.vid.value('.[1]','varchar(20)') vid
-- . means self. [1] ensure single value
from
--convert varchar(max) to xml first
(select id,cast(payload as xml) payload from #tbl) tt
cross apply
--convert xml to a tabular form
tt.payload.nodes('//x:VID') t(vid)
Results:
id vid
1 3656183
1 3656184
2 123456
2 987654
U can try this
DECLARE #Messages TABLE(ID INT, Payload NVARCHAR(MAX))
INSERT INTO #Messages(ID, Payload) VALUES(1, '<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM"> <VID>3656183</VID> </NCOAPACP>')
INSERT INTO #Messages(ID, Payload) VALUES(2, '<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM"> <VID>3656183</VID> <VID>3656184</VID> </NCOAPACP>')
;WITH CTE(ID, Payload) AS
(
SELECT ID, CAST(Payload AS XML)
FROM #Messages
)
SELECT
ID,
x.items.value('.', 'NVARCHAR(10)')
FROM CTE
CROSS APPLY Payload.nodes('declare namespace xx="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM"; xx:NCOAPACP/xx:VID') as x(items)
The most simple way (CTE test created just to get output, your xml value converted to varchar(max) as in your table, then SELECT *.value):
;WITH test AS (
SELECT CAST(
'<NCOAPACP xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="uri://www.gen.da.hob/VC/Contract/QualityManagement/EMSM">
<VID>3656183</VID>
</NCOAPACP>' as varchar(max)) as Payload
)
SELECT CAST(Payload as xml).value('(/*/*)[1]', 'int')
FROM test
Output:
3656183
I have a table, Customer(Id int,Name nvarchar(100),Detail xml)
Sample Data:
1,'Abc','<ROOT> <TAG1>False</TAG1> <TAG3>value</TAG3> <TAG14>value</TAG14> </ROOT>'
2,'Pqr','<ROOT> <TAG2>False</TAG2> <TAG8>value</TAG8> <TAG11>value</TAG11> </ROOT>'
Also I have XML variable , #v_xml = '<ROOT> <TAG1>value</TAG1> <TAG2>value</TAG2> <TAG8>False</TAG8> <TAG14>False</TAG14> </ROOT>'.
Now I want get the Missing Tags and Excess Tags (in XML format) of each Customer comparing to the XML variable #v_xml (No need to consider the value, what ever it may be)
Expected Result:
Id Name Missing Excess
1,'Abc','<ROOT><TAG2>value</TAG2> <TAG8>value</TAG8> </ROOT>','<ROOT><TAG3>value</TAG3> </ROOT>'
2,'Pqr','<ROOT><TAG1>value</TAG1> <TAG14>False</TAG14> </ROOT>','<ROOT><TAG11>value</TAG11> </ROOT>'
There is no nested nodes/level in the XML. Only direct child elements under ROOT tag. But the number of child tags will vary. I am looking for a simple and common logic to resolve this (with or without SQL query).
Main idea parse tag name (local-name(.)) and concat diffs into xml
DECLARE #t TABLE (
Id INT PRIMARY KEY,
Name VARCHAR(50),
X XML
)
INSERT INTO #t
VALUES
(1, 'Abc', N'<ROOT><TAG1>False</TAG1><TAG3>value</TAG3><TAG14>value</TAG14></ROOT>'),
(2, 'Pqr', N'<ROOT><TAG2>False</TAG2><TAG8>value</TAG8><TAG11>value</TAG11></ROOT>')
DECLARE #x XML = N'<ROOT><TAG1>value</TAG1><TAG2>value</TAG2><TAG8>False</TAG8><TAG14>False</TAG14></ROOT>'
SELECT t.Id, t.Name, t2.val.query('Missing/*'), t2.val.query('Excess/*')
FROM #t t
CROSS APPLY (
SELECT
Missing = Missing.query,
Excess = Excess.query
FROM (
SELECT
query = t.c.query('.'),
tag = t.c.value('local-name(.)', 'SYSNAME')
FROM x.nodes('*/*') t(c)
) Excess
FULL JOIN (
SELECT
query = t.c.query('.'),
tag = t.c.value('local-name(.)', 'SYSNAME')
FROM #x.nodes('*/*') t(c)
) Missing ON Missing.tag = Excess.tag
WHERE Missing.tag IS NULL
OR Excess.tag IS NULL
FOR XML PATH(''), TYPE
) t2 (val)
Output -
----------- ---------- ------------------------- ------------------------------------------
1 Abc <TAG3>value</TAG3> <TAG2>value</TAG2><TAG8>False</TAG8>
2 Pqr <TAG11>value</TAG11> <TAG1>value</TAG1><TAG14>False</TAG14>
This is my sample XML:
<root>
<element>
<subelement>
<value code="code1">value1</value>
<value code="code2">value2</value>
</subelement>
</element>
</root>
And this is my test query:
DECLARE #tempTable TABLE (
ValueCode nvarchar(MAX),
Value nvarchar(MAX)
)
DECLARE #xml XML
select #xml = cast(c1 as xml) from OPENROWSET (BULK 'C:\test.xml', SINGLE_BLOB) as T1(c1)
INSERT INTO #tempTable
SELECT
Tbl.Col.value('subelement[1]/#code', 'NVARCHAR(MAX)'),
Tbl.Col.value('subelement[1]', 'NVARCHAR(MAX)')
FROM #xml.nodes('//element') Tbl(Col)
SELECT * FROM #tempTable
The query, when executed, gives out a row with the ValueCode column containing NULL and the Value column containing 'value1value2'. What I would like to get would be the attributes and values concatenated with a separator. For example, I need ValueCode to contain 'code1; code2' and Value to contain 'value 1; value2'. How can I achieve that?
you can use xuery if you want concatenated strings:
SELECT
Tbl.Col.query('for $i in value return concat($i/text()[1], ";")').value('.', 'nvarchar(max)'),
Tbl.Col.query('for $i in value return concat($i/#code, ";")').value('.', 'nvarchar(max)')
FROM #xml.nodes('root/element/subelement') Tbl(Col);
if you want your values into rows:
SELECT
Tbl.Col.value('.', 'nvarchar(max)'),
Tbl.Col.value('#code', 'nvarchar(max)')
FROM #xml.nodes('root/element/subelement/value') Tbl(Col);
sql fiddle demo
I have a table from a vendor application that stores some xml data into a column of type varchar(200).
Table structure and sample data is here
declare #table table
(
MerchantID int not null
,Data varchar(200) not null)
insert into #table
select 1, '<product><productID>1</productID><pdesc>ProductDesc</pdesc></product>'
union all
select 2, '<product><itemid>1</itemid><itemname>name of item</itemname></product>'
Is there a way to transform raw xml data into relation format like below in a stored procedure?
for e.g when merchantID passed is 1
MerchantID productID pdesc
1 1 Product Desc
when MerchantID pass is 2 output should be
MerchantID itemid itemname
2 1 name of item
You can use XPath in SQL Server to access XML data nodes.
Here's an example, using your data.
declare #test xml
set #test = '<product><productID>1</productID><pdesc>ProductDesc</pdesc></product>'
SELECT
#test.value('(/product/productID/node())[1]', 'nvarchar(max)') as productID,
#test.value('(/product/pdesc/node())[1]', 'nvarchar(max)') as pdesc
From there, you should be able to perform your union like so:
SELECT 1,
xmlfield1.value('(/product/productID/node())[1]', 'int') as id,
xmlfield1.value('(/product/pdesc/node())[1]', 'nvarchar(max)') as desc
union
SELECT 2,
xmlfield2.value('/product/itemid/node())[1]', 'int') as id,
xmlfield2.value('/product/itemname/node())[1]', 'nvarchar(max)') as desc
if your data is in the same column, you can use a case statement to resolve it.
case
when merchantId = 1 data.value('(/product/productID/node())[1]', 'int')
else data.value('/product/itemid/node())[1]', 'int')
end as id