How to Generate xml from sql for below pattern - sql

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>

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

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 execute subquery without declaring XML?

why this query is not executing ??
SELECT [Value] = T.c.value('.','varchar(30)') FROM (SELECT '<s>'+ REPLACE ((select tag_id+',' from tbl_container_track for xml path('')),',','</s> <s>')+ '</s>').nodes('/s') T(c)
But this one is working ?
declare #X xml
SELECT #X = (SELECT '<s>'+ REPLACE ((select tag_id+',' from tbl_container_track for xml path('')),',','</s> <s>')+ '</s>')
SELECT [Value] = T.c.value('.','varchar(30)') FROM #X.nodes('/s') T(c)
Can some one help me to simplify without declaring #X ?
Try this: CAST TO XML Datatype you missed
SELECT [Value] = T.c.value('.', 'varchar(30)')
FROM (SELECT Cast(( '<s>' + Replace ((SELECT tag_id+',' FROM tbl_container_track FOR xml path('')), ',', '</s> <s>')
+ '</s>' ) AS XML)) AS Data
CROSS APPLY Data.nodes('/s') T(c)

Query XML field in SQL Server

I am using sql server 2008 R2. I have table X with Column XXML with the following structure
<rec>
<set>
<Raw CLOrderID="GGM-30-08/24/10" Rej="Preopen" Sym="A" Tm="06:36:29.524" />
</set>
</rec>
I want to parse above column XXML and return output as below:
CLOrderID Rej Sym Tm
GGM-30-08/24/10 Preopen A 06:36:29.524
Use nodes() to shred the XML and value() to extract the attribute values.
select T.X.value('#CLOrderID', 'nvarchar(100)') as CLOrderID,
T.X.value('#Rej', 'nvarchar(100)') as Rej,
T.X.value('#Sym', 'nvarchar(100)') as Sym,
T.X.value('#Tm', 'time(3)') as Tm
from dbo.X
cross apply X.XXML.nodes('/rec/set/Raw') as T(X)
If you know for sure that you only will have one row extracted for each XML you can get the attribute values directly without shredding first.
select X.XXML.value('(/rec/set/Raw/#CLOrderID)[1]', 'nvarchar(100)') as CLOrderID,
X.XXML.value('(/rec/set/Raw/#Rej)[1]', 'nvarchar(100)') as Rej,
X.XXML.value('(/rec/set/Raw/#Sym)[1]', 'nvarchar(100)') as Sym,
X.XXML.value('(/rec/set/Raw/#Tm)[1]', 'time(3)') as Tm
from dbo.X
This can be done with a few for xml calls. This structure also remains flexible for future schema changes, provided /rec/set/Raw/#* is present. If that changes, you can always add a pipe with the new path for the attributes you're wanting to grab. Hope this helps.
declare #x table (id int identity(1,1) primary key, x xml)
insert into #x (x) values ('<rec>
<set>
<Raw CLOrderID="GGM-30-08/24/10" Rej="Preopen" Sym="A" Tm="06:36:29.524" />
</set>
</rec>')
select a.id, cast((
select (
select x.attribs.value('local-name(.)','nvarchar(20)') + ' '
from #x t
outer apply t.x.nodes('/rec/set/Raw/#*') x(attribs)
where t.id = a.id
for xml path('')
), (
select x.attribs.value('.','nvarchar(20)') + ' '
from #x t
outer apply t.x.nodes('/rec/set/Raw/#*') x(attribs)
where t.id = a.id
for xml path('')
)
for xml path('')
) as varchar(500))
from #x a

SQL: How can I get the value of an attribute in XML datatype?

I have the following xml in my database:
<email>
<account language="en" ... />
</email>
I am using something like this now: but still have to find the attribute value.
SELECT convert(xml,m.Body).query('/Email/Account')
FROM Mail
How can I get the value of the language attribute in my select statement with SQL?
Use XQuery:
declare #xml xml =
'<email>
<account language="en" />
</email>'
select #xml.value('(/email/account/#language)[1]', 'nvarchar(max)')
declare #t table (m xml)
insert #t values
('<email><account language="en" /></email>'),
('<email><account language="fr" /></email>')
select m.value('(/email/account/#language)[1]', 'nvarchar(max)')
from #t
Output:
en
fr
This should work:
DECLARE #xml XML
SET #xml = N'<email><account language="en" /></email>'
SELECT T.C.value('#language', 'nvarchar(100)')
FROM #xml.nodes('email/account') T(C)
It depends a lot on how you're querying the document. You can do this, though:
CREATE TABLE #example (
document NText
);
INSERT INTO #example (document)
SELECT N'<email><account language="en" /></email>';
WITH XmlExample AS (
SELECT CONVERT(XML, document) doc
FROM #example
)
SELECT
C.value('#language', 'VarChar(2)') lang
FROM XmlExample CROSS APPLY
XmlExample.doc.nodes('//account') X(C);
DROP TABLE #example;
EDIT after changes to your question.
if the xml data is stored in sql server as a string column then use cast
select cast(your_field as XML)
.value('(/email/account/#language)[1]', 'varchar(20)')
from your_table