SQL Server XML Column - Result free of Concat - sql

I have an xml column in my SQL Server database that has records in the following format
<items>
<item>
<data alias="Number">123N</data>
<data alias="Description">4 sq.mm Feed Through Terminal block in Grey colour</data>
<data alias="Standard Packing Quantity">100</data>
</item>
<item>
<data alias="Number">234N</data>
<data alias="Description">Toy</data>
<data alias="Standard Packing Quantity">100</data>
</item>
<item>
<data alias="Number">579N</data>
<data alias="Description">Doll</data>
<data alias="Standard Packing Quantity">100</data>
</item>
<item>
<data alias="Catalouge Number">234</data>
<data alias="Description">Vehicle</data>
<data alias="Standard Packing Quantity">324234</data>
</item>
</items>
So to extract the data here I use:
SELECT
CatalogueNo,Description,StdPackingQty
from
(select
CAST(xml as xml).query('//data alias=''Description'']').value('.','nvarchar(225)') [Description],
CAST(xml as xml).query('//data [#alias=''Catalouge Number'']')
.value('.','nvarchar(225)')[CatalogueNo],
CAST(xml as xml).query('//data [#alias=''Standard Packing Quantity'']').value('.','nvarchar(225)')[StdPackingQty]
from [dbo].[cmsContentXml] )as hierarchy
Where CatalogueNo is not null
The problem I face is that the data that is extracted is all concatenated.
I need data in separate rows for each item,so the data needs to be in 3
columns and 4 rows.
Kindly help me resolve the issue asap and help write a query that would fetch data
free of concat

You can use Cross Apply to breakdown the XML into separate data rows, and pull the data from those:
SELECT
CatalogueNo,Description,StdPackingQty
from (
select
i.value('data[#alias="Description"][1]','nvarchar(225)') [Description],
i.value('data[#alias="Catalouge Number"][1]','nvarchar(225)') [CatalogueNo],
i.value('data[#alias="Standard Packing Quantity"][1]','nvarchar(225)') [StdPackingQty]
from [Connectwell].[dbo].[cmsContentXml]
cross apply xml.nodes('/items/item') x(i)
) as hierarchy
--Where CatalogueNo is not null
Note: I've remmed out the where clause at the end as it will reduce you to just one row, whereas the question state you want 4 rows as the result.

If you know that the order of your elements always are the same you can use position() to get the values:
select X.N.value('data[1]', 'nvarchar(255)') as Number,
X.N.value('data[2]', 'nvarchar(255)') as Descritpion,
X.N.value('data[3]', 'nvarchar(255)') as Quanatity
from #T as T
cross apply T.XMLColumn.nodes('/items/item') as X(N)
Otherwise you need to get the value using alias. Your fourth row has an alias "Catalouge Number" that is different from the other rows so you can either have that as a separate column:
select X.N.value('data[#alias="Number"][1]', 'nvarchar(255)') as Number,
X.N.value('data[#alias="Catalouge Number"][1]', 'nvarchar(255)') as CatalougeNumber,
X.N.value('data[#alias="Description"][1]', 'nvarchar(255)') as Descritpion,
X.N.value('data[#alias="Standard Packing Quantity"][1]', 'nvarchar(255)') as Quanatity
from #T as T
cross apply T.XMLColumn.nodes('/items/item') as X(N)
Or you can combine the two in the same column:
select X.N.value('data[#alias=("Number","Catalouge Number")][1]', 'nvarchar(255)') as Number,
X.N.value('data[#alias="Description"][1]', 'nvarchar(255)') as Descritpion,
X.N.value('data[#alias="Standard Packing Quantity"][1]', 'nvarchar(255)') as Quanatity
from #T as T
cross apply T.XMLColumn.nodes('/items/item') as X(N)
Your where clause can be added to the queries like this:
where X.N.exist('data[#alias="Catalouge Number"]') = 1
Or if you want to check against both number aliases:
where X.N.exist('data[#alias=("Number","Catalouge Number")]') = 1
Test data:
declare #T table(XMLColumn xml)
insert into #T values
('<items>
<item>
<data alias="Number">123N</data>
<data alias="Description">4 sq.mm Feed Through Terminal block in Grey colour</data>
<data alias="Standard Packing Quantity">100</data>
</item>
<item>
<data alias="Number">234N</data>
<data alias="Description">Toy</data>
<data alias="Standard Packing Quantity">100</data>
</item>
<item>
<data alias="Number">579N</data>
<data alias="Description">Doll</data>
<data alias="Standard Packing Quantity">100</data>
</item>
<item>
<data alias="Catalouge Number">234</data>
<data alias="Description">Vehicle</data>
<data alias="Standard Packing Quantity">324234</data>
</item>
</items>
')
Edit:
I see that you cast your XML column to XML. That is not necessary if the column already is of data type XML so I guess you have a varchar(max) or something in there. If so you need to do like this to cast the to XML before applying .nodes() function:
select X.N.value('data[#alias=("Number","Catalouge Number")][1]', 'nvarchar(255)') as Number,
X.N.value('data[#alias="Description"][1]', 'nvarchar(255)') as Descritpion,
X.N.value('data[#alias="Standard Packing Quantity"][1]', 'nvarchar(255)') as Quanatity
from #T as T
cross apply (select cast(XMLColumn as xml)) as X1(XMLColumn)
cross apply X1.XMLColumn.nodes('/items/item') as X(N)
where X.N.exist('data[#alias=("Number","Catalouge Number")]') = 1
Edit 2
select xml.value('data[#alias=("Number","Catalouge Number")][1]', 'nvarchar(255)') as Number,
xml.value('data[#alias="Description"][1]', 'nvarchar(255)') as Descritpion,
xml.value('data[#alias="Standard Packing Quantity"][1]', 'nvarchar(255)') as Quanatity
from [dbo].[cmsContentXml] as Hierarchy
cross apply (select cast(xml as xml)) as X1(xml)
cross apply X1.xml.nodes('/items/item') as xml(N)
where xml.N.exist('data[#alias=("Number","Catalouge Number")]')=1

Related

Save XML with attribute to Table in SQL Server

Hi I have XML data with attribute as input for SQL, i need this to be inserted in my table.
XML Data is
<?xml version="1.0" encoding="ISO-8859-1"?>
<MESSAGEACK>
<GUID GUID="kfafb30" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="1" CODE="28681" />
</GUID>
<GUID GUID="kfafb3" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="2" CODE="286381" />
</GUID>
</MESSAGEACK>
I want this to be inserted in below Format
GUID SUBMIT DATE ID ERROR SEQ CODE
kfafb3 2015-10-15 11:30:29 1 1 28681
kfafb3 2015-10-15 11:30:29 1 1 2868
please help.
Look into XPath and xml Data Type Methods in MSDN. This is one possible way :
declare #xml As XML = '...you XML string here...'
INSERT INTO YourTable
SELECT
guid.value('#GUID', 'varchar(100)') as 'GUID'
,guid.value('#SUBMITDATE', 'datetime') as 'SUBMIT DATE'
,guid.value('#ID', 'int') as 'ID'
,guid.value('ERROR[1]/#SEQ', 'int') as 'SEQ'
,guid.value('ERROR[1]/#CODE', 'int') as 'CODE'
FROM #xml.nodes('/MESSAGEACK/GUID') as x(guid)
Result :
just paste this into an empty query window and execute. Adapt to your needs:
DECLARE #xml XML=
'<?xml version="1.0" encoding="ISO-8859-1"?>
<MESSAGEACK>
<GUID GUID="kfafb30" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="1" CODE="28681" />
</GUID>
<GUID GUID="kfafb3" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="2" CODE="286381" />
</GUID>
</MESSAGEACK>';
SELECT Msg.Node.value('#GUID','varchar(max)') AS [GUID] --The value is no GUID, if the original values are, you could use uniqueidentifier instead of varchar(max)
,Msg.Node.value('#SUBMITDATE','datetime') AS SUBMITDATE
,Msg.Node.value('#ID','int') AS ID
,Msg.Node.value('(ERROR/#SEQ)[1]','int') AS [ERROR SEQ]
,Msg.Node.value('(ERROR/#CODE)[1]','int') AS CODE
FROM #xml.nodes('/MESSAGEACK/GUID') AS Msg(Node)

Querying XML data in SQL Server 2012

I have a database that has a table Parameters_XML with columns.
id, application, parameter_nr flag, value
The parameter_nr is for example 1 and the value for that parameter is the following:
<Root>
<Row>
<Item id="341" flags="1">
<Str>2</Str>
</Item>
<Item id="342" flags="1">
<Str>10</Str>
</Item>
<Item id="2196" flags="1">
<Str>7REPJ1</Str>
</Item>
</Row>
</Root>
I need to retrieve the values for all the applications where the item is 341, 342 and 2196.
Eg: for the application 1 the value for the item 341 is 2 and so on.
I have written the following query:
SELECT cast (value as XML).value('data(/Root/Row/Item[#id="431"],')
FROM Parameters_Xml x
WHERE parameter_nr = 1
I get the following error:
Msg 174, Level 15, State 1, Line 1
The value function requires 2 argument(s).
Why my query is not valid?
Try someting like this:
SELECT
CAST(x.Value AS XML).value('(/Root/Row/Item[#id="341"]/Str)[1]', 'nvarchar(100)')
FROM dbo.Parameters_Xml x
WHERE parameter_nr = 1
You're telling SQL Server to go find the <Item> node (under <Root> / <Row>) with and id=341 (that what I'm assuming - your value in the question doesn't even exist) and then get the first <Str> node under <Item> and return that value
Also: why do you need CAST(x.Value as XML) - if that column contains only XML - why isn't it defined with datatype XML to begin with? If you have this, you don't need any CAST ...
DECLARE #str XML;
SET #str = '<Root>
<Row>
<Item id="341" flags="1">
<Str>2</Str>
</Item>
<Item id="342" flags="1">
<Str>10</Str>
</Item>
<Item id="2196" flags="1">
<Str>7REPJ1</Str>
</Item>
</Row>
</Root>'
-- if you want specific values then
SELECT
xmlData.Col.value('#id','varchar(max)') Item
,xmlData.Col.value('(Str/text())[1]','varchar(max)') Value
FROM #str.nodes('//Root/Row/Item') xmlData(Col)
where xmlData.Col.value('#id','varchar(max)') = 342
--if you want all values then
SELECT
xmlData.Col.value('#id','varchar(max)') Item
,xmlData.Col.value('(Str/text())[1]','varchar(max)') Value
FROM #str.nodes('//Root/Row/Item') xmlData(Col)
--where xmlData.Col.value('#id','varchar(max)') = 342
Edit After CommentIf i query my db: select * from parameters_xml where parameter_nr = 1 i will receive over 10000 rows, each row is like the following: Id app param value 1 1 1 11 I need for all the 10000 apps to retrieve the item id and the value from the XML value - like you did for my eg.
-- declare temp table
declare #temp table
(val xml)
insert into #temp values ('<Root>
<Row>
<Item id="341" flags="1">
<Str>2</Str>
</Item>
<Item id="342" flags="1">
<Str>10</Str>
</Item>
<Item id="2196" flags="1">
<Str>7REPJ1</Str>
</Item>
</Row>
</Root>')
insert into #temp values ('<Root>
<Row>
<Item id="3411" flags="1">
<Str>21</Str>
</Item>
<Item id="3421" flags="1">
<Str>101</Str>
</Item>
<Item id="21961" flags="1">
<Str>7REPJ11</Str>
</Item>
</Row>
</Root>')
-- QUERY
SELECT
xmlData.Col.value('#id','varchar(max)') Item
,xmlData.Col.value('(Str/text())[1]','varchar(max)') Value
FROM #temp AS T
outer apply T.val.nodes('/Root/Row/Item') as xmlData(Col)
Try this:
select cast (value as
XML).value('data(/Root/Row/Item[#id="431"]','nvarchar(max)')
data type is 2nd parameter.

Processing XML Hierarchy with MS SQL

How would I specify the following XML Hierarchy into readable columns in Microsoft SQL?
<transaction id=1>
<item id=1>
<price>1</price>
</item>
<item id=2>
<price>1</price>
</item>
</transaction>
<transaction>
<item id=1>
<price>1</price>
</item>
</transaction>
for instance
select
x.i.value('(????)','Varchar(max)') [TransId]
x.i.value('(????)','Varchar(max)') [ItemId]
x.i.value('(????)','Varchar(max)') [PriceId]
from #xml.nodes('/transaction') x(i)
Thanks in advance.
Attribute values must always appear in quotation marks in XML.
Not sure about the desired output. An example would be:
declare #xml xml
Select #xml=
'<transaction id="1">
<item id="1">
<price>1</price>
</item>
<item id="2">
<price>2</price>
</item>
</transaction>
<transaction>
<item id="1">
<price>3</price>
</item>
</transaction>'
SELECT
y.value('../#id','int') as TransactionID,
y.value('#id','int') as ItemID,
y.value('(./price/text())[1]', 'Varchar(max)') as Price
FROM #xml.nodes('/transaction/item') as x(y)
order by TransactionID,ItemID
with the output:
NULL 1 3
1 1 1
1 2 2
Actually usually it's faster to shred XML from parent to child using apply, like this:
select
t.c.value('#id','int') as TransId,
i.c.value('#id','int') as ItemId,
i.c.value('(price/text())[1]', 'int') as PriceId
from #xml.nodes('transaction') as t(c)
outer apply t.c.nodes('item') as i(c)
order by TransId, ItemID
sql fiddle demo

SQL Query which deletes an attribute in XML

I have column of XML type.There's a xml like this
<items>
<item type="xxx"><items>
<item type="xxx"><items>
</items>
I need to delete all type attributes. I know oracle has some functions for xml manipulation, but I don't get how to delete attributes.
How would such query look like ?
Here is an example using the Oracle supplied SQL function deletexml
Acknowledgement to Jonas Lincoln as I am using his XPATH expression
SELECT deleteXML(xmltype.CREATEXML('<items>
<item type="xxx">a</item>
<item type="xxx">b</item>
</items>'),
'/items/item[#type="xxx"]/#type')
FROM dual
<items>
<item>a</item>
<item>b</item>
</items>
declare #xml as xml
set #xml = '
<items>
<item type="xxx">3</item>
<item type="xxx">4</item>
</items>'
SET #xml.modify('delete (/items/item[#type="xxx"]/#type)')
select cast(#xml as nvarchar(100))
<items>
<item>3</item>
<item>4</item>
</items>

SQL XML parsing different nodes

i would like to ask you for help with parsing XML in SQL, where my XML looks like this, where Load is parrent which can be repeated X-times. I need Column SerNr and for each row need to bound Order name
where final table will looks like this
Example of table:
<ImageHistory>
<Load targets="2" totalTime="417">
<Orders>
<Order name="20548976"/>
</Orders>
<Data>
<Disk SerNr="XXXXXX" Size_mb="228936" LoadSuccessfull="true" />
<Disk SerNr="ZZZZZ" Size_mb="228936" LoadSuccessfull="true" />
</Data>
</Load>
</ImageHistory>
sql is
with data as (SELECT CAST(MY_XML AS xml) as MY_XML FROM OPENROWSET(BULK 'addres to xml', SINGLE_BLOB) AS T(MY_XML)),
datainfo as (
SELECT
MY_XML.Blasting.value(' #name', 'BIGINT') as Size_mb,
MY_XML.Blasting.value('#SerNr', 'varchar(32)') as SerNr
FROM data CROSS APPLY MY_XML.nodes('ImageHistory/Load/Data/Disk') AS MY_XML (Blasting))
select * from datainfo
thank you for help
I saved the XML as a file on the file system: e:\Temp\DiskSerialNumbers.xml
The rest is below.
SQL
;WITH XmlFile (Contents) AS
(
SELECT CAST(BulkColumn AS XML)
FROM OPENROWSET(BULK 'e:\Temp\DiskSerialNumbers.xml', SINGLE_BLOB) AS XmlData
)
SELECT c.value('(../../Orders/Order/#name)[1]', 'INT') AS [Name]
, c.value('#SerNr', 'VARCHAR(20)') AS [SerNr]
FROM XmlFile CROSS APPLY Contents.nodes('/ImageHistory/Load/Data/Disk') AS t(c);
Output
+----------+--------+
| Name | SerNr |
+----------+--------+
| 20548976 | XXXXXX |
| 20548976 | ZZZZZ |
+----------+--------+
You've got your answer already, but I want to point to the fact, that backward navigation (using ../../) is horribly slow with bigger structures.
I'd suggest this:
A mockup to simulate your issue:
DECLARE #yourTable TABLE(ID INT IDENTITY,YourXml XML);
INSERT INTO #yourTable VALUES
(N'<ImageHistory>
<Load targets="2" totalTime="417">
<Orders>
<Order name="20548976" />
</Orders>
<Data>
<Disk SerNr="XXXXXX" Size_mb="228936" LoadSuccessfull="true" />
<Disk SerNr="ZZZZZ" Size_mb="228936" LoadSuccessfull="true" />
</Data>
</Load>
</ImageHistory>');
--The query
SELECT t.ID
,ld.value('#targets','int') AS Load_Targets
,ld.value('#totalTime','int') AS Load_TotalTime
,ld.value('(Orders/Order/#name)[1]','int') AS Order_Name
,dsk.value('#SerNr','nvarchar(100)') AS Disk_SerNr
,dsk.value('#Size_mb','nvarchar(100)') AS Disk_Size_mb
,dsk.value('#LoadSuccessfull','bit') AS Disk_LoadSuccessfull
FROM #yourTable AS t
CROSS APPLY t.YourXml.nodes('/ImageHistory/Load') A(ld)
CROSS APPLY A.ld.nodes('Data/Disk') B(dsk);
The idea in short:
The first APPLY will dive down to <Load> and will return all <Load> elements (if there are more...
The second APPLY will use the fragment returned by the first APPLY and dive deeper down to <Disk>.
We fetch the order's name (and other values) calling .value() against the first fragment (which is the <Load> element) and we fetch the values of <Disk> calling .value() against the fragment of the second APPLY.