Concatenate XML variables in SQL Server - sql

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

Related

Transform a SELECT * query to string

I have a query that returns a row
SELECT *
FROM table
WHERE id = 1;
I want to save the result into a nvarchar sql variable. I have seen similar questions Convert SQL Server result set into string but they only use select with the name of the columns, never with *.
select *
from table
where id = 1
for xml path ('')
However the answer is <column1>value1</column1> <column2>value2</column2> and I just want it to be value1, value2
Is there a way to achieve this? thank you!
If open to a helper function.
This will convert virtually any row, table or query to a string (delimited or not).
In the following examples I selected a PIPE delimiter with a CRLF line terminator.
Please note the usage and placement of _RN when a line terminator is required. Also note the ,ELEMENTS XSINIL ... this will included null values as empty string. If you want to exclude null values, simply omit the ,ELEMENTS XSINIL
Example as Entire Table or dbFiddle
Declare #YourTable Table (id int,[col_1] varchar(50),[col_2] varchar(50),[col_3] varchar(50),[col_n] varchar(50)) Insert Into #YourTable Values
(1,'data1','data2','data3','data4')
,(2,'data5','data6','data7','data8')
-- Entire Table
Declare #XML xml = (Select *,_RN=Row_Number() over (Order By (Select null)) From #YourTable for XML RAW,ELEMENTS XSINIL )
Select [dbo].[svf-str-Data-To-Delimited]('|',char(13)+char(10),#XML)
Returns
1|data1|data2|data3|data4
2|data5|data6|data7|data8
Example as Row Based
Select A.ID
,AsAString = [dbo].[svf-str-Data-To-Delimited]('|',char(13)+char(10),B.XMLData)
From #YourTable A
Cross Apply ( values ( (select a.* for xml RAW,ELEMENTS XSINIL )) )B(XMLData)
Returns
ID AsAString
1 1|data1|data2|data3|data4
2 2|data5|data6|data7|data8
The Function if Interested
CREATE Function [dbo].[svf-str-Data-To-Delimited] (#Delim varchar(50),#EOL varchar(50),#XML xml)
Returns varchar(max)
Begin
Return(
Select convert(nvarchar(max),(
Select case when Item='_RN' then ''
else case when nullif(lead(Item,1) over (Order by Seq),'_RN') is not null
then concat(Value,#Delim)
else concat(Value,#EOL)
end
end
From (
Select Seq = row_number() over(order by (select null))
,Item = xAttr.value('local-name(.)', 'nvarchar(100)')
,Value = xAttr.value('.','nvarchar(max)')
From #XML.nodes('/row/*') xNode(xAttr)
) A
Order By Seq
For XML Path (''),TYPE).value('.', 'nvarchar(max)') )
)
End
You can easily store the result as an XML string:
select *
from (values (1, 'x', getdate())) v(id, a, b)
where id = 1
for xml path ('');
Or as a JSON string:
select *
from (values (1, 'x', getdate())) v(id, a, b)
where id = 1
for json auto;
If you don't mind Using dynamic SQL (and INFORMATION_SCHEMA dictionary), for example, for SQL Server this works:
DECLARE #sql nvarchar(max) = '',
#result nvarchar(max),
#id int = 1
SELECT #sql += '+'',''+convert(nvarchar,' + QUOTENAME(column_name) +')' from INFORMATION_SCHEMA.columns where table_name = 'Student'
SET #sql = 'select #result=' + stuff(#sql,1,5,'') + ' from student where id = ' + CAST(#id as nvarchar)
EXECUTE sp_executesql #sql, N'#result nvarchar(max) OUTPUT', #result=#result OUTPUT
SELECT #result as MyOutput

Sql Server - For XML - Get null value from element

I am trying to replace the null xml element into the null value while doing the concatenation. And i am making some silly mistake. I want to differentiate between an empty value and null value. I am using OpenXML to parse the XML data and something is missing in the code to read the null based param element.
I am using Server Server 2014.
Please suggest.
DECLARE #message_body XML;
DECLARE #XMLParameterData Table
(SeqID INT Identity(1,1),
ParamValue varchar(max))
DECLARE #docRef int
DECLARE #dataPath nvarchar(255)
DECLARE #mappingType int = 2 --Element-Centric mapping
Select #message_body = N'<AsyncRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ParamList> <Param>Bruce</Param>
<Param>Wa''yne</Param>
<Param>Bruce#karan.com</Param>
<Param>Coke</Param>
<Param>20000</Param>
<Param xsi:nil="true"/>
<Param></Param>
</ParamList>
</AsyncRequest>';
Set #dataPath = '/AsyncRequest/ParamList/Param'
EXEC sp_xml_preparedocument #docRef output, #message_body
INSERT INTO #XMLParameterData(ParamValue)
Select * From OpenXML(#docRef, #dataPath, #mappingType)
WITH
(
valx varchar(max) '.'
)
-- the xml document ref needs to be released ASAP
EXEC sp_xml_removedocument #docRef
SELECT * From #XMLParameterData
DECLARE #CSVString varchar(max)
SELECT #CSVString = STUFF(
(SELECT ', ' +
CHAR(34) + ParamValue + CHAR(34)
FROM #XMLParameterData
ORDER BY SeqID
FOR XML PATH('')
), 1, 1, '')
SELECT #CSVString as CSVTest
Output :-
"Bruce", "Wa'yne", "Bruce#karan.com", "Coke", "20000", "", ""
Desired output :-
"Bruce", "Wa'yne", "Bruce#karan.com", "Coke", "20000", NULL, ""
Keep it simple! Use CASE WHEN to check if #xsi:nil="true" and .nodes instead of OPENXML:
DECLARE #message_body XML,
#output nvarchar(max);
select #message_body = N'<AsyncRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ParamList>
<Param>Bruce</Param>
<Param>Wa''yne</Param>
<Param>Bruce#karan.com</Param>
<Param>Coke</Param>
<Param>20000</Param>
<Param xsi:nil="true"/>
<Param></Param>
</ParamList>
</AsyncRequest>';
SELECT #output = STUFF((
SELECT
CASE WHEN t.v.value('#xsi:nil','nvarchar(max)') = 'true' THEN ',NULL'
ELSE ',"'+t.v.value('.','nvarchar(max)') + '"'
END
FROM #message_body.nodes('AsyncRequest/ParamList/Param') as t(v)
FOR XML PATH('')
),1,1,'')
SELECT #output
Will return:
"Bruce","Wa'yne","Bruce#karan.com","Coke","20000",NULL,""
How about this (I have slightly simplified your code by using xml.nodes rather than an xml document).
It uses the xml query expression .[not(#xsi:nil = "true")] to return a null where xsi:nil is true.
I then use COALESCE to return the string 'NULL' when a NULL is returned:
DECLARE #message_body XML;
DECLARE #XMLParameterData Table
(SeqID INT Identity(1,1),
ParamValue varchar(max))
Select #message_body = N'<AsyncRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ParamList><Param>Bruce</Param><Param>Wa''yne</Param>
<Param>Bruce#karan.com</Param>
<Param>Coke</Param>
<Param>20000</Param>
<Param xsi:nil="true"/>
<Param></Param>
</ParamList>
</AsyncRequest>';
INSERT INTO #XMLParameterData(ParamValue)
SELECT T.c.value('.[not(#xsi:nil = "true")]', 'varchar(max)') AS result
FROM #message_body.nodes('/AsyncRequest/ParamList/Param')T(c)
SELECT * From #XMLParameterData
DECLARE #CSVString varchar(max)
SELECT #CSVString = STUFF(
(SELECT ', ' +
CHAR(34) + COALESCE(ParamValue, 'NULL') + CHAR(34)
FROM #XMLParameterData
ORDER BY SeqID
FOR XML PATH('')
), 1, 1, '')
SELECT #CSVString as CSVTest
This returns:
"Bruce", "Wa'yne", "Bruce#karan.com", "Coke", "20000", "NULL", ""
What are you trying to achieve is not a standard behavior. You propbably expect following:
DECLARE #message_body XML = N'<AsyncRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ParamList>
<Param>Bruce</Param>
<Param>Wa''yne</Param>
<Param>Bruce#karan.com</Param>
<Param>Coke</Param>
<Param>20000</Param>
<Param xsi:nil="true"/>
<Param></Param>
</ParamList>
</AsyncRequest>';
SELECT X.value('.[not(#xsi:nil="true")]', 'nvarchar(MAX)') Value
FROM #message_body.nodes('//Param') T(X)
Which yields:
Value
-----
Bruce
Wa'yne
Bruce#karan.com
Coke
20000
NULL
(empty string here)
You may want text from nodes, which is more standarized:
SELECT X.value('text()[1]', 'nvarchar(MAX)') Value
FROM #message_body.nodes('//Param') T(X)
Value
-----
Bruce
Wa'yne
Bruce#karan.com
Coke
20000
NULL
NULL
Note that <element/> and <element></element> are synonyms. It's empty, no matter how you write. Ask yourself: is first <element/> empty string? That would lead to long discussion - it's all a matter of interpretation. You may also consider xml:space attribute to handle whitespaces.

Get Last values using Select Substring in SQL Query

I have a table with following Column
Name
----------------------
test10/20000020
test1/test2 / 20000001
test3/test4 / 20000002
test5/20000017
test5/test6 / 20000004
test5/20000007
I need to select the last value
I.e
20000020
20000001
20000002
20000017
20000004
20000007
I have tried with
(SUBSTRING(Name,0,(CHARINDEX('/',Name,0))))
But I am getting First Value
i.e. test1
test2
test3
Using XML:
declare #xml xml;
declare #str nvarchar(max) = '';
with data as
(
select 'test1/test2/test3/344495' as r
union ALL
select 'test1/344556' as r
)
select #str = #str + '<r><v>' + replace(r,'/','</v><v>') + '</v></r>'
from data;
-- obtain xml
set #xml = cast(#str as xml);
-- select last value from each row
select v.value('(v/text())[last()]', 'nvarchar(50)')
from #xml.nodes('/r') as r(v)
Same idea but without variables:
;with data as
(
select 'test1/test2/test3/344495' as r
union ALL
select 'test1/344556' as r
),
xmlRows AS
(
select cast('<r><v>' + replace(r,'/','</v><v>') + '</v></r>' as xml) as r
from data
)
select v.value('(v/text())[last()]', 'nvarchar(50)') as lastValue
from xmlRows xr
cross APPLY
r.nodes('/r') as r(v)
SELECT SUBSTRING( Name, LEN(Name) - CHARINDEX('/',REVERSE(Name)) + 2 , LEN(Name) ) FROM SAMPLE
worked for me
SUBSTRING ( expression ,start , length )
Instead of that
(SUBSTRING(Name,0,(CHARINDEX('/',Name,0))))
You should use
(SUBSTRING(Name,(CHARINDEX('/',Name,0)+1),8))
if string size is fixed.
You could solve your problem with the following query:
SELECT (SUBSTRING(Name,(CHARINDEX('/',Name,0)),LEN(Name)))

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)')

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>