XQuery SQL XML values - sql

We have to find a query parsing XML to have a result like this one:
COL_ACTION N COL_NAME_I COL_VALUE_AFTER
---------- ----------- ---------------- -------------------
INS 1 N 1
INS 1 TST_ID 28
INS 1 TST_DATA data2
INS 2 N 2
INS 2 TST_ID 27
INS 2 TST_DATA data1
The value of column N depend on the value of the first "row" (COL_NAME_I = N) of the XML dataset
The XML contains:
DECLARE #XML XML =
N'
<DATASET>
<XML_INS>
<IROW xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<N>1</N>
<TST_ID>28</TST_ID>
<TST_DATA>data2</TST_DATA>
</IROW>
<IROW xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<N>2</N>
<TST_ID>27</TST_ID>
<TST_DATA>data1</TST_DATA>
</IROW>
</XML_INS>
</DATASET>
';
The query I was able to do is:
SELECT RIGHT(ca.value('local-name(.)','CHAR(7)'), 3) AS COL_ACTION,
x.value('(//N)[1]', 'int') AS N,
-- cn.value('./text()[1]','int') AS NI,
ci.value('local-name(.)','sysname') AS COL_NAME_I,
ci.value('./text()[1]','nvarchar(max)') AS COL_VALUE_AFTER
FROM #XML.nodes('.') AS T(x)
OUTER APPLY #XML.nodes('DATASET/*') AS TA(ca)
OUTER APPLY #XML.nodes('DATASET/XML_INS/IROW/N/*') AS TN(cn)
OUTER APPLY #XML.nodes('DATASET/XML_INS/IROW/*') AS TI(ci);
The problem remains on the "N" column that does no have the expected value:
COL_ACTION N COL_NAME_I COL_VALUE_AFTER
---------- ----------- ---------------- -------------------
INS 1 N 1
INS 1 TST_ID 28
INS 1 TST_DATA data2
INS 1 N 2
INS 1 TST_ID 27
INS 1 TST_DATA data1
I tryed different approach, as seen in SQL comment, but does not produce the good result...

You need each .nodes to refer to the previous, in order to only bring back its children. Otherwise you get every descendant node for the whole document.
SELECT RIGHT(action.value('local-name(.)','CHAR(7)'), 3) AS COL_ACTION,
irow.value('(N/text())[1]', 'int') AS N,
ci.value('local-name(.)','sysname') AS COL_NAME_I,
ci.value('text()[1]','nvarchar(max)') AS COL_VALUE_AFTER
FROM #XML.nodes('DATASET/*') AS x1(action)
OUTER APPLY x1.action.nodes('IROW') AS x2(irow)
OUTER APPLY x2.irow.nodes('*') AS TI(ci);
db<>fiddle

This should do it:
SELECT
irow.value('local-name(..)', 'NVARCHAR(100)') AS COL_ACTION,
irow.value('(./N)[1]', 'NVARCHAR(100)') AS N,
child.value('local-name(.)', 'NVARCHAR(100)') AS COL_NAME_I,
child.value('.', 'NVARCHAR(100)') AS COL_VALUE_AFTER
FROM #XML.nodes('/DATASET/*/IROW') AS n1(irow)
CROSS APPLY irow.nodes('./*') AS n2(child)

i am a new contributer so sorry if i got something wrong.
I used this approach on my work : Link
My sample working code :
CREATE TABLE #FieldPermissionsTable
(
TE_ID VARCHAR(MAX),
Value VARCHAR(MAX),
Value2 VARCHAR(MAX),
NAVI_USER VARCHAR(MAX)
)
--SET #FieldPermissionAccessXML = '<Row_ID><Elements ID="1" Value="1" Value2="NULLnROLLA" Navi_User="testuser" /><Elements ID="3" Value="tespit" Value2="NULLnROLLA" Navi_User="testuser" /><Elements ID="6" Value="aciklama" Value2="NULLnROLLA" Navi_User="testuser" /></Row_ID>'
-- SELECT #FieldPermissionAccessXML = [dbo].[to_xml_replace] (#FieldPermissionAccessXML)
EXEC sp_xml_preparedocument #XmlHandle output,#FieldPermissionAccessXML
--'<FieldPermissions>
--<FieldPermission FieldName="" RoleID="1" Access="1" />
--<FieldPermission FieldName="" RoleID="2" Access="1" />'
--select * from JMP_FieldPermissions
INSERT INTO #FieldPermissionsTable
SELECT *--ID,Value, Value2, Navi_User
FROM OPENXML (#XmlHandle, './Row_ID/Elements')
WITH (TE_ID VARCHAR(MAX) '#ID',
Value VARCHAR(MAX) '#Value',
Value2 VARCHAR(MAX) '#Value2',
NAVI_USER VARCHAR(MAX) '#Navi_User'
)

Related

How to parse XML ordred list into table view

How to parse XML ordered list like this
<ol>
<li>value1</li>
<li>value2</li>
<li>value3</li>
</ol>
into table like this (like it's visible in html):
Nr Value
----------- ------
1 value1
2 value2
3 value3
Here is the code for XML string:
declare #ol XML= '<ol><li>'+REPLACE('value1,value2,value3', ',', '</li><li>')+'</li></ol>'
select #ol
NB! Is it possible to parse "numbering" from XML without creating something like identity column?
Small update:
Following solutions provide right answer for simple example above:
Yitzhak Khabinsky, Salman A
akhilesh singh
But is it possible to get solution for this more tricky example:
DECLARE #ol XML
SET #ol=
'<ol type="i" start="3">
<li>value1</li>
<li>value2</li>
<li>value3</li>
</ol>';
Estimated result:
Nr Value
---- -------
iii value1
iv value2
v value3
?
Finding the nodes is easy, finding their relative position is tricky. Here is one solution by using what is called Node Comparison operation via << operator and count function:
DECLARE #ol XML = '<ol>
<li>value1</li>
<li>value2</li>
<li>value3</li>
</ol>';
SELECT li.value('.', 'NVARCHAR(100)') AS value
, li.value('let $n := . return count(../*[. << $n]) + 1', 'int') AS pos
FROM #ol.nodes('/ol/li') AS x(li)
DECLARE #ol XML
SET #ol= '<ol>
<li>value1</li>
<li>value2</li>
<li>value3</li>
</ol>';
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS Nr,
li.value('.', 'NVARCHAR(100)') AS Value
FROM #ol.nodes('/ol/li') AS x(li)
Try this Simple Query...here...Getting Row Number ()...so we get Nr as Number...1,2,3..so on...and xml value into table....
You will get this type of Output...
Output :
Nr Value
----------- ------
1 value1
2 value2
3 value3
After Edit Your Question...the Solution Should be Like this.....
DECLARE #ol XML
SET #ol=
'<ol type="i" start="3">
<li>value1</li>
<li>value2</li>
<li>value3</li>
</ol>';
SELECT 2 + ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS Nr,
li.value('.', 'NVARCHAR(100)') AS Value
FROM #ol.nodes('/ol/li') AS x(li)
Output :
Nr Value
----------- ------
3 value1
4 value2
5 value3
here adding +2 value to RowNumber() so....we can get the value 2,3,4...and so on..

Get values from XML field with SQL Server

I would like to get the value from a xml column with T-SQL.
My xml column named XML contains :
<SIMBATCHJOB ONFAIL="2" ID="4" ONPASS="0"/>
How can I get the second parameter? For example here ID.
I have tried a request like this:
DECLARE #xml XML
SELECT #xml = N'<SIMBATCHJOB ONFAIL="2" ID="4" ONPASS="0"/>'
SELECT
T.C.value('(SIMBATCHJOB)[1]', 'int') as test1
FROM
#xml.nodes('SIMBATCHJOB') T(C)
My table is structured like this:
ID | XML
If I try this query:
Select ID
,Value = cast([xml] as xml).value('SIMBATCHJOB[1]/#ID','int')
From SIM_JOBS
where id = 179
This query return me the ID and the Value is empty
Do you understand this ?
Thanks in advance
Jeroen is 100% correct, but one option is to use a CROSS APPLY and normalize your attributes.
The Sequence will hold true to the sequence in the actual XML. I suspect you could filter as needed.
Example
Declare #XML xml = '<SIMBATCHJOB ONFAIL="2" ID="4" ONPASS="0"/>'
Select Seq = row_number() over (Order By (Select NULL))
,Attribute = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)')
From #XML.nodes('/SIMBATCHJOB ') as C1(n)
Cross Apply C1.n.nodes('./#*') as C2(a)
Returns
Seq Attribute Value
1 ONFAIL 2
2 ID 4
3 ONPASS 0
EDIT - Update for Comments ---
This will return the SECOND Attribute/Value regardles of what is is
Declare #YourTable table (ID int,[xml] xml)
Insert Into #YourTable values
(1,'<SIMBATCHJOB ONFAIL="2" ID="4" ONPASS="0"/>'),
(2,'<SIMBATCHJOB ONFAIL="2" ID="99" ONPASS="1"/>')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select *
From (
Select Seq = row_number() over (Order By (Select NULL))
,Attribute = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)')
From A.xml.nodes('/SIMBATCHJOB ') as C1(n)
Cross Apply C1.n.nodes('./#*') as C2(a)
) B1
Where B1.Seq=2
) B
Returns
ID Seq Attribute Value
1 2 ID 4
2 2 ID 99
EDIT 2: - Get JUST ID regardless of Sequence
Declare #YourTable table (ID int,[xml] xml)
Insert Into #YourTable values
(1,'<SIMBATCHJOB ONFAIL="2" ID="4" ONPASS="0"/>'),
(2,'<SIMBATCHJOB ONFAIL="2" ID="99" ONPASS="1"/>')
Select A.ID
,Value = [xml].value('SIMBATCHJOB[1]/#ID','int')
From #YourTable A
Returns
ID Value
1 4
2 99

SQL Script to Break up XML into Columns

I have a table that stores audit history in an XML format. Each document has its own history. How can I parse out he XML data per document whereby each column in the XML represents and an actual column and action that took place in that column.
Example:
<auditElement><field id="2881159" type="5" name="Responsiveness" formatstring=""><unSetChoice>2881167</unSetChoice><setChoice>2881166</setChoice></field></auditElement>
UnsetChoice and Set Choice are the columns.
Name=represents the action.
Xml can be parsed using features such as XQuery, or OpenXml.
Here's an example of xquery:
declare #xml as xml = '<auditElement><field id="2881159" type="5" name="Responsiveness"' +
' formatstring=""><unSetChoice>2881167</unSetChoice>' +
'<setChoice>2881166</setChoice></field></auditElement>';
SELECT
Nodes.node.value('(field/#id)[1]', 'INT') AS FieldId,
Nodes.node.value('(field/#name)[1]', 'varchar(50)') AS FieldName,
Nodes.node.value('(field/unSetChoice/text())[1]', 'INT') AS OldValue,
Nodes.node.value('(field/setChoice/text())[1]', 'INT') AS NewValue
FROM
#xml.nodes('//auditElement') AS Nodes(node);
Result:
FieldId FieldName OldValue NewValue
----------- -------------------------------------------------- ----------- -----------
2881159 Responsiveness 2881167 2881166
You can use OUTER APPLY and then break down the parts of the XML field.
The value() method takes XQuery expressions.
For example:
DECLARE #T TABLE (Id int identity(1,1) primary key, XmlCol1 xml);
insert into #T (XmlCol1) values
('<auditElement><field id="2881159" type="5" name="Responsiveness" formatstring=""><unSetChoice>2881167</unSetChoice><setChoice>2881166</setChoice></field></auditElement>'),
('<auditElement><field id="2881160" type="6" name="Responsiveness" ><unSetChoice>2881187</unSetChoice><setChoice>2881188</setChoice></field></auditElement>');
select *
from (
select
Id,
a.field.value('#id', 'int') as xmlid,
a.field.value('(unSetChoice)[1]', 'int') as unSetChoice,
a.field.value('(setChoice)[1]', 'int') as setChoice,
a.field.value('#type', 'int') as type,
a.field.value('#name', 'varchar(max)') as name,
a.field.value('#formatstring', 'varchar(max)') as formatstring
from #T t
outer apply t.XmlCol1.nodes('/auditElement/field') as a(field)
where a.field.value('#id', 'int') > 0 and Id > 0
) q
where name = 'Responsiveness';
Result:
Id xmlid unSetChoice setChoice type name formatstring
1 2881159 2881167 2881166 5 Responsiveness
2 2881160 2881187 2881188 6 Responsiveness NULL

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>

XQuery and Node Ids

I have this variable:
declare #xmlDoc XML
it has the following xml stored in it:
<?xml version="1.0" encoding="utf-8"?>
<NewDataSet>
<Table1>
<Sharedparam>shared</Sharedparam>
<Antoher>sahre</Antoher>
<RandomParam2>Good stuff</RandomParam2>
<MoreParam>and more</MoreParam>
<ResultsParam>2</ResultsParam>
</Table1>
<Table1>
<RandomParam2>do you</RandomParam2>
<MoreParam>think</MoreParam>
<ResultsParam>2</ResultsParam>
</Table1>
<Table1>
<Sharedparam>Last</Sharedparam>
<Antoher> Set </Antoher>
<RandomParam2> of </RandomParam2>
<MoreParam>values</MoreParam>
<ResultsParam>are here</ResultsParam>
</Table1>
<Table1 />
</NewDataSet>
I have this query that I am using to get the data:
declare #xmlDoc XML
set #xmlDoc = '' -- Stack Overflow could not handle the xml all on one line.
SELECT -- Param 1
TBL.SParam.value('local-name((*)[1])', 'varchar(50)') as Param1Name,
TBL.SParam.value('(*)[1]', 'varchar(100)') as Param1Value,
-- Param2
TBL.SParam.value('local-name((*)[2])', 'varchar(50)') as Param2Name,
TBL.SParam.value('(*)[2]', 'varchar(100)') as Param2Value,
-- Param3
TBL.SParam.value('local-name((*)[3])', 'varchar(50)') as Param3Name,
TBL.SParam.value('(*)[3]', 'varchar(100)') as Param3Value,
-- Param 4
TBL.SParam.value('local-name((*)[4])', 'varchar(50)') as Param4Name,
TBL.SParam.value('(*)[4]', 'varchar(100)') as Param4Value,
-- Param 5
TBL.SParam.value('local-name((*)[5])', 'varchar(50)') as Param5Name,
TBL.SParam.value('(*)[5]', 'varchar(100)') as Param5Value
FROM #xmldoc.nodes('/NewDataSet/Table1') AS TBL(SParam)
I need a way to add to my results the order that they came from the xml file. (Which was the first instance of Table1, then the second....).
For reasons of SQL table variable limitations, I can't use an identity column to keep this straight. (For other reasons, I don't want to use a temporary table.)
I am hoping that there is a cool SQL XML function that will return some kind of internally assigned Node ID. (Or some other similar manner of ordering.)
Note, I do not control this XML Structure (I am a reader only) so I cannot make changes to add in an ID attribute.
Any advice would be great!
EDIT/Update:
I would really like to have this data like this:
1 | SharedParam | shared
1 | Antoher | sahre
1 | RandomParam2 | Good stuff
1 | MoreParam | and more
1 | ResultsParam | and more
2 | RandomParam2 | do you
2 | MoreParam | think
2 | ResultsParam | 2
3 | Sharedparam | Last
3 | Antoher | Set
.
.
.
But I am coming up short. I can get it into columns (more or less), but I don't know how to do the numbering. If you have any ideas I would love to hear them.
EDIT:
I figured out the query to do this (with some help from the internet). It looks like this:
SELECT TBL.SParam.value('local-name(.)[1]', 'varchar(50)') as ParamName,
TBL.SParam.value('(.)[1]', 'varchar(50)') ParamValue,
TBL.SParam.value('for $s in . return count(../*[. << $s]) + 1', 'int') ParamPosition,
TBL.SParam.value('for $s in . return count(../../*[. << $s]) - 1', 'int') ParamIteration
FROM #xmldoc.nodes('/NewDataSet/Table1/*') AS TBL(SParam)
You can use a number table and position()
SELECT N.Number as ID,
-- Param 1
TBL.SParam.value('local-name((*)[1])', 'varchar(50)') as Param1Name,
TBL.SParam.value('(*)[1]', 'varchar(100)') as Param1Value,
-- Param2
TBL.SParam.value('local-name((*)[2])', 'varchar(50)') as Param2Name,
TBL.SParam.value('(*)[2]', 'varchar(100)') as Param2Value,
-- Param3
TBL.SParam.value('local-name((*)[3])', 'varchar(50)') as Param3Name,
TBL.SParam.value('(*)[3]', 'varchar(100)') as Param3Value,
-- Param 4
TBL.SParam.value('local-name((*)[4])', 'varchar(50)') as Param4Name,
TBL.SParam.value('(*)[4]', 'varchar(100)') as Param4Value,
-- Param 5
TBL.SParam.value('local-name((*)[5])', 'varchar(50)') as Param5Name,
TBL.SParam.value('(*)[5]', 'varchar(100)') as Param5Value
FROM master..spt_values as N
cross apply #xmldoc.nodes('/NewDataSet/Table1[position()=sql:column("N.Number")]') AS TBL(SParam)
where N.type = 'P' and
N.number between 1 and #xmlDoc.value('count(/NewDataSet/Table1)', 'int')
Take a look at this connect request to Fully support position() in xquery The requestor offers a couple of work arounds that might be usful to you
Based on the second workaround I wrote the following
SELECT
p.number,
TBL.SParam.value('local-name((*)[1])', 'varchar(50)') as Param1Name,
TBL.SParam.value('(*)[1]', 'varchar(100)') as Param1Value,
-- Param2
TBL.SParam.value('local-name((*)[2])', 'varchar(50)') as Param2Name,
TBL.SParam.value('(*)[2]', 'varchar(100)') as Param2Value,
-- Param3
TBL.SParam.value('local-name((*)[3])', 'varchar(50)') as Param3Name,
TBL.SParam.value('(*)[3]', 'varchar(100)') as Param3Value,
-- Param 4
TBL.SParam.value('local-name((*)[4])', 'varchar(50)') as Param4Name,
TBL.SParam.value('(*)[4]', 'varchar(100)') as Param4Value,
-- Param 5
TBL.SParam.value('local-name((*)[5])', 'varchar(50)') as Param5Name,
TBL.SParam.value('(*)[5]', 'varchar(100)') as Param5Value
FROM
master..spt_values p CROSS APPLY
#xmldoc.nodes('NewDataSet/Table1[position()=sql:column("p.number")]') AS TBL(SParam)
WHERE P.type = 'P'
Which returns the following
number Param1Name Param1Value Param2Name Param2Value Param3Name Param3Value Param4Name Param4Value Param5Name Param5Value
----------- -------------------------------------------------- ---------------------------------------------------------------------------------------------------- -------------------------------------------------- ---------------------------------------------------------------------------------------------------- -------------------------------------------------- ---------------------------------------------------------------------------------------------------- -------------------------------------------------- ---------------------------------------------------------------------------------------------------- -------------------------------------------------- ----------------------------------------------------------------------------------------------------
1 Sharedparam shared Antoher sahre RandomParam2 Good stuff MoreParam and more ResultsParam 2
2 RandomParam2 do you MoreParam think ResultsParam 2 NULL NULL
3 Sharedparam Last Antoher Set RandomParam2 of MoreParam values ResultsParam are here
4 NULL NULL NULL NULL NULL
(4 row(s) affected)