XML Comparison(String) in SQL server - sql

I have two XML strings in two separate tables.
**ROOT**
**Node ID=** 1
**name** vignesh **/name**
**street** 1211 **/street**
**/Node**
**Node ID=** 2
**name** ram **/name**
**street** 333 **/street**
**/Node**
**/ROOT**
**ROOT**
**Node ID=** 1
**name** newbie **/name**
**street** 121223 **/street**
**/Node**
**Node ID=** 2
**name** pro **/name**
**street** 445**/street**
**/Node**
**/ROOT**
Please help me to find the comparison between these two XMLs in SQL Server and give result as Old value and New value for both ID1 and ID2(Node).

I believe this will help you out . i build a proper XML structure by considering your XML data.
DECLARE #XMLString1 XML =
'<ROOT>
<Nodes>
<NodeID>1</NodeID>
<name>vignesh </name>
<street>1211</street>
</Nodes>
<Nodes>
<NodeID>2</NodeID>
<name>ram</name>
<street>333</street>
</Nodes>
</ROOT>' ,
#XMLString2 XML =
'<ROOT>
<Nodes>
<NodeID>1</NodeID>
<name>newbie </name>
<street>121223</street>
</Nodes>
<Nodes>
<NodeID>2</NodeID>
<name>pro</name>
<street>445</street>
</Nodes>
</ROOT>'
SELECT #XMLString1,#XMLString2
;WITH XMLDATA1 AS
(
SELECT T.X.value('NodeID[1]','varchar(50)') AS NODEID,
T.X.value('name[1]','varchar(50)') AS NAME,
T.X.value('street[1]','varchar(50)') AS STREET
FROM #XMLString1.nodes('/ROOT/Nodes')T(X)
)
,XMLDATA2 AS
(
SELECT T.X.value('NodeID[1]','varchar(50)') AS NODEID,
T.X.value('name[1]','varchar(50)') AS NAME,
T.X.value('street[1]','varchar(50)') AS STREET
FROM #XMLString2.nodes('/ROOT/Nodes')T(X)
)
SELECT X1.NODEID,X1.NAME AS OLD_NAME,X2.NAME AS NEW_NAME , X1.STREET AS OLD_STREET , X2.STREET AS NEW_STREET
FROM XMLDATA1 X1
INNER JOIN XMLDATA2 X2 ON X1.NODEID=X2.NODEID

Related

Convert XML data into a table SQL, Different tag in one script

I have one XML data which I want to bring them into a one table
the XML data is like this:
<return>
<start>
<name>Sara</name>
<familyname>Moradi</familyname>
<age>22</age>
</start>
<start>
<name>Sam</name>
<familyname>Mic</familyname>
<age>32</age>
</start>
<errorCode>0</errorCode>
<resultStatus/>
<extra>22255</extra>
</return>
and I wanna create a table like this:
name
familyname
age
errorCode
extra
Sara
Moradi
22
0
22255
Sam
Mic
32
0
22255
I check the previous ones but they didn't help me.
Try below:
declare #data xml = convert(xml, '<return>
<start>
<name>Sara</name>
<familyname>Moradi</familyname>
<age>22</age>
</start>
<start>
<name>Sam</name>
<familyname>Mic</familyname>
<age>32</age>
</start>
<errorCode>0</errorCode>
<resultStatus/>
<extra>22255</extra>
</return>')
SELECT X.Y.value('(name)[1]', 'VARCHAR(20)') as name,
X.Y.value('(familyname)[1]', 'VARCHAR(20)') as familyname,
X.Y.value('(age)[1]', 'int') as age,
A.B.value('(errorCode)[1]','int') as errorCode,
A.B.value('(extra)[1]','int') as extra
FROM #data.nodes('return') as A(B)
cross apply A.B.nodes('start') as X(Y)
For quick solution, I have stored xml data into variable, you need to replace it with your original column from table.

Stuck in T-SQL : XML to temp table

I have a little challenge where I am currently stuck in.
This is my query:
DECLARE #xml XML
SET #xml =
'<beginmsg>
<refmsg>A1234567</refmsg>
<shipments>
<shipment>
<load>
<address>
<name>Loadname</name>
<street>Street</street>
<zipcode>ABCDE</zipcode>
<city>Munchen</city>
<countrycode>DE</countrycode>
</address>
<dateTime>
<date>2021-02-02</date>
<timeFrom>08:00:00</timeFrom>
</dateTime>
</load>
<unload>
<address>
<name>unloadname</name>
<street>Street unload</street>
<zipcode>1111</zipcode>
<city>Dresden</city>
<countrycode>DE</countrycode>
</address>
</unload>
<goods>
<good>
<description>Cookies</description>
<quantity>1</quantity>
</good>
<good>
<description>Cookies</description>
<quantity>3</quantity>
</good>
<good>
<description>Some food</description>
<quantity>2</quantity>
</good>
</goods>
<barcodes>
<barcode>7829348742910092309325</barcode>
<barcode>7829348742112344114414</barcode>
<barcode>0984746166149566188446</barcode>
</barcodes>
<references>
<reference>GBP-4362</reference>
</references>
</shipment>
<shipment>
<load>
<address>
<name>shipment 2 load</name>
<street>Street load2</street>
<zipcode>1234 RR</zipcode>
<city>Koln</city>
<countrycode>DE</countrycode>
</address>
<dateTime>
<date>2021-03-03</date>
<timeFrom>10:00:00</timeFrom>
</dateTime>
</load>
<unload>
<address>
<name>Shipment 2 unload</name>
<street>Street 2 unl</street>
<zipcode>1000 AA</zipcode>
<city>Amsterdam</city>
<countrycode>NL</countrycode>
</address>
</unload>
<goods>
<good>
<description>Televisions</description>
<quantity>2</quantity>
</good>
</goods>
<barcodes>
<barcode>0984746166149566188446</barcode>
</barcodes>
<references>
<reference>HBR-7211CX</reference>
</references>
</shipment>
</shipments>
</beginmsg>'
IF OBJECT_ID('tempdb..#Goods') IS NOT NULL DROP TABLE #Goods;
CREATE TABLE #Goods
(
row INT,
shipmentreference VARCHAR(100),
description VARCHAR(MAX),
quantity INT,
barcode VARCHAR(MAX)
);
INSERT INTO #Goods
SELECT
"row" = ROW_NUMBER() OVER (PARTITION BY shipment.shipment ORDER BY shipment.shipment),
"shipmentreference" = CAST(shipment.shipment.value('(references/reference)[1]', 'VARCHAR(100)') AS VARCHAR(MAX)) + '-' + CAST(ROW_NUMBER() OVER (PARTITION BY shipment.shipment ORDER BY shipment.shipment) AS VARCHAR(MAX)),
"description" = goods.goods.value('(description)[1]', 'VARCHAR(10)'),
"quantity" = goods.goods.value('(quantity)[1]', 'VARCHAR(10)'),
"barcode" = barcode.barcode.value('(barcode)[1]', 'VARCHAR(100)')
FROM
#Xml.nodes('beginmsg/shipments/shipment') shipment(shipment)
CROSS APPLY
shipment.shipment.nodes('goods/good') goods(goods)
CROSS APPLY
shipment.shipment.nodes('barcodes') barcode(barcode)
SELECT * FROM #Goods
The XML contains 2 shipments.
In the 1st shipment there are 3 goods segment with each 1 'good' segment. In the 2nd shipment there is one good segment.
I would like to merge each good, with their information and with the 1st barcode for the 1st good, 2nd barcode for the 2nd good etc. etc.
So I was thinking to create a TEMP table #goods and a temp table to store the barcodes in. In both temp tables I would like to add the "reference" so I can join both tables on that unique field.
But at this moment I am stuck. In my final query (as shown above) the output is as follow:
row
reference
description
quantity
barcode
1
GBP-4362-1
Cookies
1
7829348742910092309325
2
GBP-4362-1
Cookies
3
7829348742910092309325
3
GBP-4362-1
Some food
2
7829348742910092309325
1
HBR-7211CX-1
Television
2
0984746166149566188446
So the barcode is each first (what is actually what I am querying :))
How can I iterate through these barcodes nodes and how can I manage to merge it into the row?

XML element value with width limitation

I am new to XML and trying to solve the following in SQL Server 2008 using customer table.
NAME column has fixed width, so the value (customer name) needs to be separated into more than one representation.
Please see:
NAME index="1"....
NAME index="2"....
Any idea how to tackle this?
Thank you,
Anne
<PARTNER>
<NAME index="1">XEXSY SMALL REALTY LLC</NAME>
<NAME index="2">AA/NAX TEEEENERGY</NAME>
<PARTNRTYPE>703884</PARTNRTYPE>
<ADDRESS>
<ADDRLINE index="1">544 PACIFIC BLVD</ADDRLINE>
<CITY>LONG BEACH</CITY>
<COUNTRY>US</COUNTRY>
<POSTALCODE>07740</POSTALCODE>
</ADDRESS>
</PARTNER>
This design is awfull. If you have to the slightest chance to change this, you should...
If you have to stick with this, you can try it like this:
DECLARE #mockup TABLE(Name VARCHAR(100),PartnerType INT,Addr VARCHAR(100),City VARCHAR(100));
INSERT INTO #mockup VALUES
('This is a very long name which needs to be splitted in smaller parts'
,12345
,'And this address is very long too, the person has a really long address...'
,'Washington')
,('ShortName'
,12345
,'ShortAddress'
,'New York');
--You can set the length of the slices. The TOP(20) is hardcoded and sets an upper limit to the count of parts.
DECLARE #PartLenght INT=20;
--The query will get a number's table (tally table) on-the-fly and then use FOR XML PATH() to create the XML with the nestings you need.
WITH Tally AS
(
SELECT TOP(20) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Nr
FROM master..spt_values
)
SELECT (
SELECT Nr AS [NAME/#index]
,SUBSTRING(m.Name,Nr+((Nr-1) * (#PartLenght-1)),#PartLenght) AS [NAME]
FROM Tally
WHERE LEN(SUBSTRING(m.Name,Nr+((Nr-1) * (#PartLenght-1)),#PartLenght))>0
FOR XML PATH(''),TYPE
)
,m.PartnerType AS [PARTNERTYPE]
,(
SELECT
(
SELECT Nr AS [ADDRLINE/#index]
,SUBSTRING(m.Addr,Nr+((Nr-1) * (#PartLenght-1)),#PartLenght) AS [ADDRLINE]
FROM Tally
WHERE LEN(SUBSTRING(m.Addr,Nr+((Nr-1) * (#PartLenght-1)),#PartLenght))>0
FOR XML PATH(''),TYPE
)
,City AS [CITY]
FOR XML PATH('ADDRESS'),TYPE
)
FROM #mockup AS m
FOR XML PATH('PARTNER')
The result
<PARTNER>
<NAME index="1">This is a very long </NAME>
<NAME index="2">name which needs to </NAME>
<NAME index="3">be splitted in small</NAME>
<NAME index="4">er parts</NAME>
<PARTNERTYPE>12345</PARTNERTYPE>
<ADDRESS>
<ADDRLINE index="1">And this address is </ADDRLINE>
<ADDRLINE index="2">very long too, the p</ADDRLINE>
<ADDRLINE index="3">erson has a really l</ADDRLINE>
<ADDRLINE index="4">ong address...</ADDRLINE>
<CITY>Washington</CITY>
</ADDRESS>
</PARTNER>
<PARTNER>
<NAME index="1">ShortName</NAME>
<PARTNERTYPE>12345</PARTNERTYPE>
<ADDRESS>
<ADDRLINE index="1">ShortAddress</ADDRLINE>
<CITY>New York</CITY>
</ADDRESS>
</PARTNER>

SELECTing multiple values for the same attribute using FOR XML in SQL

I'm attempting to retrieve data from a SQL table as XML. Consider the following table:
Customers
Id Last First Phone1 Phone2
10 Doe John 555-2121 555-6145
33 Smith Sally 555-3333 555-7000
I'd like to retrieve the Last, First, Phone1, and Phone2 fields using the FOR XML clause in SQL. However, I'd like to retrieve the Phone2 field with an attribute of "Evening". For example, I'd like the XML to look like:
<Customers>
<Customer>
<Last>Doe</Last>
<First>John</First>
<Phone Type="Daytime">555-2121</Phone>
<Phone Type="Evening">555-6145</Phone>
</Customer>
</Customers>
However, I'm unable to get it to work. Is there something I'm missing? Any and all help would be greatly appreciated!
You can build the nodes in a subquery. If you specify for xml type, the subqueries merge with the outer query to give one XML:
select Last as [Last]
, First as [First]
, (
select 'Daytime' as [#Type]
, Phone1 as [*]
for xml path('Phone'), type
)
, (
select 'Evening' as [#Type]
, Phone2 as [*]
for xml path('Phone'), type
)
from Customers
for xml path('Customer'), root('Customers')
This prints:
<Customers>
<Customer>
<Last>Doe</Last>
<First>John</First>
<Phone Type="Daytime">555-2121</Phone>
<Phone Type="Evening">555-6145</Phone>
</Customer>
<Customer>
<Last>Smith</Last>
<First>Sally</First>
<Phone Type="Daytime">555-3333</Phone>
<Phone Type="Evening">555-7000</Phone>
</Customer>
</Customers>
Live example at SQL Fiddle.

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.