How do I select data from a XML column in a SQL Server table in SQL Server Management Studio?
I would like to get this result:
orderDdate createdBby orderNo currency taxation inv customer mail
2019-09-05 storefront 000001 USD gross 0099999 Jonh Smith JonhSmith#gmail.com
<orders xmlns="www address">
<order>
<order-date>2019-09-05</order-date>
<created-by>storefront</created-by>
<original-order-no>000001</original-order-no>
<currency>USD</currency>
<taxation>gross</taxation>
<invoice-no>0099999</invoice-no>
<customer>
<customer-name>Jonh Smith</customer-name>
<customer-email>JonhSmith#gmail.com</customer-email>
</customer>
<notes>
<note>
<created-by>system</created-by>
<creation-date>2019-09-06T07:05:03.000Z</creation-date>
<subject>Fulfilment Status</subject>
<text>The order fulfilment status was changed from '01' to '02'.</text>
</note>
<note>
<created-by>system</created-by>
<creation-date>2019-09-06T07:05:03.000Z</creation-date>
<subject>Fulfilment Status</subject>
<text>The order fulfilment status was changed from '02' to '03'.</text>
</note>
<note>
<created-by>system</created-by>
<creation-date>2019-09-06T07:05:03.000Z</creation-date>
<subject>Fulfilment Status</subject>
<text>The order fulfilment status was changed from '03' to '03'.</text>
</note>
</notes>
<product-lineitems>
<product-lineitem>
<product-id>0001</product-id>
<quantity unit="BOX">1.0</quantity>
<tax-rate>0.23</tax-rate>
</product-lineitem>
<product-lineitem>
<product-id>0002</product-id>
<quantity unit="PCS">1.0</quantity>
<tax-rate>0.23</tax-rate>
</product-lineitem>
</product-lineitems>
</order>
I have the last problem (i hope) ;)
How to return data from product-lineitems. I mean: Quantity, Quantity-unit
Im trying like that: But it's return duplicate rows :(
WITH XMLNAMESPACES(DEFAULT N'www address')
SELECT
o.value(N'(current-order-no/text())[1]',N'varchar(10)') AS OrderNo
,n.value(N'(quantity/text())[1]',N'varchar(10)') AS Qty
,u.value(N'#unit',N'varchar(10)') AS Unit
FROM
dbSupply.dbo.MyXmlTable t
CROSS APPLY
t.XMLData.nodes(N'/orders/order') A(o)
OUTER APPLY
A.o.nodes(N'product-lineitems/product-lineitem') B(n)
OUTER APPLY
A.o.nodes(N'product-lineitems/product-lineitem/quantity') C(u)
'It looks like your post is mostly code; please add some more details'
'It looks like your post is mostly code; please add some more details'
'It looks like your post is mostly code; please add some more details'
You can try it like this:
DECLARE #mockupTable TABLE(ID INT IDENTITY, YourXml XML);
INSERT INTO #mockupTable VALUES
(N'<orders xmlns="www address">
<order order-no="000001">
<order-date>2019-09-05</order-date>
<created-by>storefront</created-by>
<original-order-no>000001</original-order-no>
<currency>USD</currency>
<taxation>gross</taxation>
<invoice-no>0099999</invoice-no>
<customer>
<customer-name>Jonh Smith</customer-name>
<customer-email>JonhSmith#gmail.com</customer-email>
</customer>
</order>
</orders>');
WITH XMLNAMESPACES(DEFAULT N'www address')
SELECT o.value(N'#order-no',N'varchar(10)') AS OrderNo
,o.value(N'(order-date/text())[1]',N'date') AS OrderDate
,o.value(N'(created-by/text())[1]',N'varchar(100)') AS CreatedBy
,o.value(N'(original-order-no/text())[1]',N'varchar(10)') AS OriginalOrderNo
,o.value(N'(currency/text())[1]',N'varchar(10)') AS Currency
,o.value(N'(taxation/text())[1]',N'varchar(10)') AS Taxation
,o.value(N'(invoice-no/text())[1]',N'varchar(10)') AS InvoiceNo
,o.value(N'(customer/customer-name/text())[1]',N'varchar(100)') AS CustomerName
,o.value(N'(customer/customer-email/text())[1]',N'varchar(100)') AS CustomerEMail
FROM #mockupTable t
CROSS APPLY t.YourXml.nodes(N'/orders/order') A(o);
Be aware of the need to declare your (default) name space correctly. I used .nodes() because "orders" sounds like plural. Your sample includes just one order, but there might be more...
UPDATE: Your additional "notes"
In any case of 1:n relationship (many nodes related to one partent node), you need .nodes() in order to get each fragment as a separate row:
WITH XMLNAMESPACES(DEFAULT N'www address')
SELECT o.value(N'#order-no',N'varchar(10)') AS OrderNo
,o.value(N'(order-date/text())[1]',N'date') AS OrderDate
,o.value(N'(created-by/text())[1]',N'varchar(100)') AS CreatedBy
,o.value(N'(original-order-no/text())[1]',N'varchar(10)') AS OriginalOrderNo
,o.value(N'(currency/text())[1]',N'varchar(10)') AS Currency
,o.value(N'(taxation/text())[1]',N'varchar(10)') AS Taxation
,o.value(N'(invoice-no/text())[1]',N'varchar(10)') AS InvoiceNo
,o.value(N'(customer/customer-name/text())[1]',N'varchar(100)') AS CustomerName
,o.value(N'(customer/customer-email/text())[1]',N'varchar(100)') AS CustomerEMail
,n.value(N'(created-by/text())[1]',N'nvarchar(100)') AS Note_CreatedBy
,n.value(N'(creation-date/text())[1]',N'datetime') AS Note_CreatedBy
,n.value(N'(subject/text())[1]',N'nvarchar(100)') AS Note_CreatedBy
,n.value(N'(text/text())[1]',N'nvarchar(1000)') AS Note_CreatedBy
FROM #mockupTable t
CROSS APPLY t.YourXml.nodes(N'/orders/order') A(o)
OUTER APPLY A.o.nodes(N'notes/note') B(n);
The last line will pick the current order and pass it into .nodes(). This will return a derived set with one row per <note>.
You can use OPENROWSET
CREATE DATABASE MyXmlDataBase
GO
USE MyXmlDataBase
GO
CREATE TABLE MyXmlTable
(
Id INT IDENTITY PRIMARY KEY,
Name NVARCHAR(50),
XMLData XML
)
INSERT INTO MyXmlTable(Name, XMLData)
SELECT CONVERT(XML, BulkColumn) AS BulkColumn, GETDATE()
FROM OPENROWSET(BULK 'D:\MyXmlFileOnDisk.xml', SINGLE_BLOB) AS x;
SELECT * FROM MyXmlTable
Related
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?
I've found several pieces of information online about this but I can't get it working for the life of me.
This is the XML I have:
I need to extract the ID & Name value for each node. There are a lot.
I tried to do this but it returns NULL:
select [xml].value('(/Alter/Object/ObjectDefinition/MeasureGroup/Partitions/Partition/ID)[1]', 'varchar(max)')
from test_xml
I understand the above would return only 1 record. My question is, how do I return all records?
Here's the XML text (stripped down version):
<Alter xmlns="http://schemas.microsoft.com/analysisservices/2003/engine" AllowCreate="true" ObjectExpansion="ExpandFull">
<ObjectDefinition>
<MeasureGroup xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>ts_homevideo_sum_20140430_76091ba1-3a51-45bf-a767-f9f3de7eeabe</ID>
<Name>table_1</Name>
<StorageMode valuens="ddl200_200">InMemory</StorageMode>
<ProcessingMode>Regular</ProcessingMode>
<Partitions>
<Partition>
<ID>123</ID>
<Name>2012</Name>
</Partition>
<Partition>
<ID>456</ID>
<Name>2013</Name>
</Partition>
</Partitions>
</MeasureGroup>
</ObjectDefinition>
</Alter>
You need something like this:
DECLARE #MyTable TABLE (ID INT NOT NULL, XmlData XML)
INSERT INTO #MyTable (ID, XmlData)
VALUES (1, '<Alter xmlns="http://schemas.microsoft.com/analysisservices/2003/engine" AllowCreate="true" ObjectExpansion="ExpandFull">
<ObjectDefinition>
<MeasureGroup xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>ts_homevideo_sum_20140430_76091ba1-3a51-45bf-a767-f9f3de7eeabe</ID>
<Name>table_1</Name>
<StorageMode valuens="ddl200_200">InMemory</StorageMode>
<ProcessingMode>Regular</ProcessingMode>
<Partitions>
<Partition>
<ID>123</ID>
<Name>2012</Name>
</Partition>
<Partition>
<ID>456</ID>
<Name>2013</Name>
</Partition>
</Partitions>
</MeasureGroup>
</ObjectDefinition>
</Alter>')
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/analysisservices/2003/engine')
SELECT
tbl.ID,
MeasureGroupID = xc.value('(ID)[1]', 'varchar(200)'),
MeasureGroupName = xc.value('(Name)[1]', 'varchar(200)'),
PartitionID = xp.value('(ID)[1]', 'varchar(200)'),
PartitionName = xp.value('(Name)[1]', 'varchar(200)')
FROM
#MyTable tbl
CROSS APPLY
tbl.XmlData.nodes('/Alter/ObjectDefinition/MeasureGroup') AS XT(XC)
CROSS APPLY
XC.nodes('Partitions/Partition') AS XT2(XP)
WHERE
ID = 1
First of all, you must respect and include the default XML namespace defined in the root of your XML document.
Next, you need to do a nested call to .nodes() to get all <MeasureGroup> and all contained <Partition> nodes, so that you can reach into those XML fragments and extract the ID and Name from them.
This should then result in something like this as output:
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>
I am using this query:
select
SalesOrderID, OrderDate, DueDate, rowguid
from
Sales.SalesOrderHeader
where
SalesOrderID between 43661 and 43662
for xml path('Order'), Root('AllOrders')
The output of this is:
<AllOrders>
<Order>
<SalesOrderID>43661</SalesOrderID>
<OrderDate>2005-07-01T00:00:00</OrderDate>
<DueDate>2005-07-13T00:00:00</DueDate>
<rowguid>D91B9131-18A4-4A11-BC3A-90B6F53E9D74</rowguid>
</Order>
<Order>
<SalesOrderID>43662</SalesOrderID>
<OrderDate>2005-07-01T00:00:00</OrderDate>
<DueDate>2005-07-13T00:00:00</DueDate>
<rowguid>4A1ECFC0-CC3A-4740-B028-1C50BB48711C</rowguid>
</Order>
</AllOrders>
I want this as expected output (xmlns:xsd added):
<AllOrders xmlns:xsd="some schema URL">
<Order>
<SalesOrderID>43661</SalesOrderID>
<OrderDate>2005-07-01T00:00:00</OrderDate>
<DueDate>2005-07-13T00:00:00</DueDate>
<rowguid>D91B9131-18A4-4A11-BC3A-90B6F53E9D74</rowguid>
</Order>
<Order>
<SalesOrderID>43662</SalesOrderID>
<OrderDate>2005-07-01T00:00:00</OrderDate>
<DueDate>2005-07-13T00:00:00</DueDate>
<rowguid>4A1ECFC0-CC3A-4740-B028-1C50BB48711C</rowguid>
</Order>
</AllOrders>
What modification do I make to query to achieve the above output?
You can use XMLNAMESPACES;
WITH XMLNAMESPACES ('http://foo.com/bar' AS xsd)
select SalesOrderID,OrderDate,DueDate...
In general you can always convert to nvarchar(max) and do text replacements.
This means you can do it like this
select replace(cast((
select SalesOrderID,OrderDate,DueDate,rowguid from Sales.SalesOrderHeader
where SalesOrderID between 43661 and 43662
for xml path('Order'), Root('AllOrders')
) as nvarchar(max)), N'<AllOrders>', N'<AllOrders xmlns:xsd="http://foo.com/bar">')
Although I would much prefer you do it as per the suggestion from #AlexK
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.