How to parse XML from table in SQL Server - sql-server

I have data in XML in column in table
SELECT ObjectXML
FROM DispOps_Events
[ObjectXML] [nvarchar](max) NOT NULL
A sample of the XML data:
<Document>
<DocumentId>3352597</DocumentId>
<DocumentFullPath>xxx</DocumentFullPath>
<Category>xxx</Category>
<ClientId>xxx</ClientId>
<ApplicationNumber>xxx</ApplicationNumber>
<ContractNumber>xxx</ContractNumber>
<Created>xxx</Created>
<Creator>xxx</Creator>
</Document>
And I need get data from DocumentId>XXXX/DocumentId> and insert into #tmpTable.
So 1. I cast varchar(max) to xml
select CAST(ObjectXML as XML) as fileXML
INTO #tmpXML
FROM DispOps_Events T WHERE MetastormMapName = 'DocumentsMap'
I tried
select
m.c.value('#DocumentId', 'varchar(max)') as DocumentId
--into #tmpTable
from #tmpXML as s
outer apply s.fileXML.nodes('Document/DocumentId') as m(c)
Error:
null data in the table

You don't need #temp tables to do this, you can just cast the nvarchar(max) data to the xml data type in a single query, e.g.:
/*
* Setup test data...
*/
drop table if exists dbo.DispOps_Events;
create table dbo.DispOps_Events (
ID int not null identity(1,1),
ObjectXML nvarchar(max)
);
insert dbo.DispOps_Events (ObjectXML) values
(N'<Document><DocumentId>2554742</DocumentId><!--...--></Document>'),
(N'<Document><DocumentId>2576868</DocumentId><!--...--></Document>'),
(N'<Document><DocumentId>2576869</DocumentId><!--...--></Document>'),
(N'<Document><DocumentId>2576870</DocumentId><!--...--></Document>');
/*
* Query XML...
*/
select ID, [DocumentId] = Document.DocumentId.value('text()[1]', 'nvarchar(50)')
from dbo.DispOps_Events
cross apply ( select try_cast(ObjectXML as xml) ) Transformers(RoolyTroolyXml)
cross apply RoolyTroolyXml.nodes('/Document/DocumentId') as Document(DocumentId);
ID
DocumentId
1
2554742
2
2576868
3
2576869
4
2576870

Related

Insert multiple rows of data with out looping the table data

I have a table where it holds some duplicate entries, I would like to copy over the distinct entries to another table with out looping the data. I need to check if the distinct data exists in other table and insert what ever is missing. Here is the query I am writing, I feel like it can be implement better
CREATE TABLE ForgeRock
([productName] varchar(13));
INSERT INTO ForgeRock
([productName])
VALUES
('OpenIDM'), ('OpenAM'), ('OpenDJ'), ('OpenDJ'),('OpenDJ1');
CREATE TABLE ForgeRock1
([productName] varchar(13));
DECLARE #prodName NVARCHAR(MAX)
SELECT DISTINCT #prodName = STUFF((SELECT ',' + productName
FROM ForgeRock
FOR XML PATH('')) ,1,1,'')
set #prodName = ''''+replace(#prodName,',',''',''')+''''
INSERT INTO ForgeRock1 (productName)
SELECT DISTINCT productName FROM ForgeRock WHERE
productName NOT IN (SELECT productName FROM ForgeRock1
where productName NOT IN (#prodName))
Here is the sample fiddle I tried out http://sqlfiddle.com/#!18/9dbe8f/1/0, is this query efficient or can it be better
This query should do what you want :)
INSERT INTO ForgeRock1 (productName)
SELECT DISTINCT productName FROM ForgeRock fr
WHERE NOT EXISTS ( SELECT 1 FROM ForgeRock1 fr1 WHERE fr1.productName = fr.productName )

Multiple XML tag value into single column with comma separator

I have an XML where the XML have multiple similar tag and I want this value need to show in one column with comma separator and insert into table.
For example:
<test xmlns="http://www.google.com">
<code>a</code>
<code>b</code>
<code>c</code>
</test>
Since XML is too large and I am using OPENXML to perform operation and insert that value into particular table.
I am performing like
insert into table A
(
code
)
select Code from OPENXML(sometag)
with (
code varchar(100) 'tagvalue'
)
for XQUERY I am using something like this: 'for $i in x:Code return concat($i/text()[1], ";")' and I want same with OPENXML.
Output: I want code tag value into one column like a,b,c or a/b/c.
Since you're on SQL Server 2017 you could use STRING_AGG (Transact-SQL) to concatenate your code values, e.g.:
create table dbo.Test (
someTag xml
);
insert dbo.Test (someTag) values
('<test><code>a</code><code>b</code><code>c</code></test>'),
('<test><code>d</code><code>e</code><code>f</code></test>');
select [Code], [someTag]
from dbo.Test
outer apply (
select [Code] = string_agg([value], N',')
from (
select n1.c1.value('.', 'nvarchar(100)')
from someTag.nodes(N'/test/code') n1(c1)
) src (value)
) a1;
Which yields...
Code someTag
a,b,c <test><code>a</code><code>b</code><code>c</code></test>
d,e,f <test><code>d</code><code>e</code><code>f</code></test>
Just a small tweak to AlwaysLearning (+1)
Example
Declare #YourTable table (ID int,XMLData xml)
insert Into #YourTable values
(1,'<test><code>a</code><code>b</code><code>c</code></test>')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select DelimString = string_agg(xAttr.value('.','varchar(max)'),',')
From A.XMLData.nodes('/test/*') xNode(xAttr)
) B
Returns
ID DelimString
1 a,b,c
And just for completeness, here is method #3 via pure XQuery and FLWOR expression.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata xml);
INSERT #tbl (xmldata) VALUES
('<test xmlns="http://www.google.com"><code>a</code><code>b</code><code>c</code></test>'),
('<test xmlns="http://www.google.com"><code>d</code><code>e</code><code>f</code></test>');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ',';
-- Method #3
-- SQL Server 2005 onwards
;WITH XMLNAMESPACES (DEFAULT 'http://www.google.com')
SELECT ID
, xmldata.query('for $i in /test/code
return if ($i is (/test/code[last()])[1]) then string($i)
else concat($i, sql:variable("#separator"))')
.value('.', 'NVARCHAR(MAX)') AS [Comma_separated_list]
FROM #tbl;
Output
+----+----------------------+
| ID | Comma_separated_list |
+----+----------------------+
| 1 | a, b, c |
| 2 | d, e, f |
+----+----------------------+

How to get an xml attribute value from a table's column in a select list

DECLARE #xml AS XML
SET #xml = CONVERT(xml,'<data><UserType userID="123">employee</UserType></data>')
SELECT (SELECT d.value('#userID', 'int')
FROM #xml.nodes('//data/UserType') T(d))
I have a table where the column is like the XML above. Is it possible to get the #userID value in a select statement?
In my Users table, the column 'XmlData' is of type XML.
SELECT
userID -- u.XmlData
FROM Users u
How can I grab the userID attribute from the xml in a select statement? I know how to parse it once, but not in a select.
You knit those together with APPLY. Something like:
SELECT
u.*,
SELECT T.d.value('#userID', 'int') userID
FROM Users u
CROSS APPLY u.XmlData.nodes('/data/UserType') T(d)
Same idea like John Cappelletti, just with CTE for conversion to XML.
SQL
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, XMLData NVARCHAR(MAX));
INSERT INTO #tbl (XMLData)
VALUES
(N'<data><UserType userID="123">employee</UserType></data>');
;WITH rs AS
(
SELECT *, TRY_CAST(XMLData AS XML) AS xml_data FROM #tbl
)
SELECT ID
, col.value('#userID','INT') AS userID
FROM rs
CROSS APPLY rs.[xml_Data].nodes('/data/UserType') AS tab(col);
Output
+----+--------+
| ID | userID |
+----+--------+
| 1 | 123 |
+----+--------+
With a little twist.
Example
Declare #YourTable table (ID int,XMLData nvarchar(max))
Insert Into #YourTable values
(1,'<data><UserType userID="123">employee</UserType></data>')
Select A.ID
,C.*
From #YourTable A
Cross Apply ( values (convert(XML,XMLData) )) B(XData)
Cross Apply ( Select UserID = d.value('#userID', 'int')
From XData.nodes('/data/UserType') T(d)
) C
Returns
ID UserID
1 123
Update If only ONE User ID in the XML
Declare #YourTable table (ID int,XMLData nvarchar(max))
Insert Into #YourTable values
(1,'<data><UserType userID="123">employee</UserType></data>')
Select A.ID
,UserID = convert(XML,XMLData).value('/data[1]/UserType[1]/#userID', 'int')
From #YourTable A

Grouping XML Elements in FOR XML Clause

I am trying to create a structure xml document from my temp table .The temp table is in the following format .
CREATE TABLE #Temp1 ( Name Char( 30 ), seqid integer, salary int );
INSERT INTO #Temp1 VALUES('DEAL' ,123,6)
INSERT INTO #Temp1 VALUES('DEAL' ,56,6)
INSERT INTO #Temp1 VALUES('TRACNHE' ,1253,56)
INSERT INTO #Temp1 VALUES('TRACNHE' ,5,65)
INSERT INTO #Temp1 VALUES('ASSET' ,56,23)
I am trying to create an xml format in the following form :
<Response>
<Deal>
<seqid="123" salary="6" />
<seqid="56" salary="6" />
<Deal>
<TRACNHE>
<seqid="1253" salary="56"/>
<seqid="5" salary="65"/>
</TRACNHE>
<ASSET>
<seqid="56" salary="23"/>
</ASSET>
</Response>
SELECT Name, (SELECT SEQID FROM #TEMP1 T WHERE T.Name = T1.Name)
FROM (SELECT DISTINCT NAME FROM #TEMP1 ) T1
FOR XML PATH('rEPONSE')
DROP TABLE #Temp1
DROP TABLE #Temp1
I tried the above query but says that subquery returned more than 1 value
Could you let me know as to what i am missing in this query .
Is there a better way to handle this scenario.
Thanks in advance
based on your requirement, i'm seeing there are 2 types of complexities
You are trying to get the xml with grouped items.
For each group trying to create an xml element with two attributes
without any proper name
<seqid="1253" salary="56"/>
instead of
<ss seqid="1253" salary="56"/>
just look into this below query, it may help
SELECT
(SELECT
seqid 'ss/#seqid'
, salary 'ss/#salary'
FROM Temp1 as t where t.Name = 'Deal'
FOR XML PATH('Deal') , TYPE
) ,
(SELECT
seqid 'ss/#seqid'
, salary 'ss/#salary'
FROM Temp1 as t where t.Name = 'TRACNHE'
FOR XML PATH('TRACNHE') , TYPE
) ,
(SELECT
seqid 'ss/#seqid'
, salary 'ss/#salary'
FROM Temp1 as t where t.Name = 'ASSET'
FOR XML PATH('ASSET') , TYPE
)
FOR XML PATH(''), ROOT('Response');

T- SQL transform unstructured XML into columns

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

Resources