Deserialize XML data into a table [duplicate] - tsql

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Deserialize XML data object in T-SQL
I've got this XML object:
<params>
<item>
<idtype>1</idtype>
<name>dsf2</name>
<value>2012-10-05 23:59:59</value>
</item>
<item>
<idtype>2</idtype>
<name>msm1</name>
<value>999</value>
</item>
</params>
How can i save value data into a table like this one:

DECLARE #XML XML = '<params>
<item>
<idtype>1</idtype>
<name>dsf2</name>
<value>2012-10-05 23:59:59</value>
</item>
<item>
<idtype>2</idtype>
<name>msm1</name>
<value>999</value>
</item>
</params>'
INSERT INTO [TableName] ([idtype], [name], [value])
SELECT
[idtype] = TypeNode.value('(idtype)[1]', 'int'),
[name] = TypeNode.value('(name)[1]', 'nvarchar(50)'),
[value] = TypeNode.value('(value)[1]', 'nvarchar(50)')
FROM
#XML.nodes('/params/item') AS XTbl(TypeNode)
BTW: I assume your table is defined following way:
CREATE TABLE [TableName](
[idtype] [int] NULL,
[name] [nvarchar](50) NULL,
[value] [nvarchar](50) NULL
)

Related

Postgres use xpath_table parsing with xmlnamespaces

Can I use xpath_table parsing with xmlnamespaces
drop table if exists _xml;
create temporary table _xml (fbf_xml_id serial,str_Xml xml);
insert into _xml(str_Xml)
select '<DataSet1 xmlns="http://tempuri.org/DataSet_LocalMaMC.xsd">
<Stations>
<ID>5</ID>
</Stations>
<Stations>
<ID>1</ID>
</Stations>
<Stations>
<ID>2</ID>
</Stations>
<Stations>
<ID>10</ID>
</Stations>
<Stations>
<ID/>
</Stations>
</DataSet1>' ;
drop table if exists _y;
create temporary table _y as
SELECT *
FROM xpath_table('FBF_xml_id','str_Xml','_xml',
'/DataSet1/Stations/ID',
'true') AS t(FBF_xml_id int,ID text);
select * from _y
If I take of the xmlnamespaces it works fine.
I thought to work with Xpath, but when there is null, it gives me wrong results.
With Postgres 10 or later, xmltable() is the preferred way to do this.
You can easily specify a list of namespaces with that.
SELECT fbf_xml_id, xt.id
FROM _xml
cross join
xmltable(xmlnamespaces ('http://tempuri.org/DataSet_LocalMaMC.xsd' as x),
'/x:DataSet1/x:Stations'
passing str_xml
columns
id text path 'x:ID') as xt
Note that in the XPath expression used for the xmltable() function, the tags are prefixed with the namespace alias defined in the xmlnamespaces option even though they are not prefixed in the input XML.
Online example

OPENXML Leaves out xml rows that do not have the specified elements

I am running below query on SQL Server 2014 server but I can't understand why I am getting only one row in result.
DECLARE #idoc int, #doc varchar(1000);
SET #doc ='
<ROOT>
<Customer CustomerID="VINET" ContactName="Paul Henriot">
<Order CustomerID="VINET" EmployeeID="5" OrderDate="1996-07-04T00:00:00">
<OrderDetail OrderID="10248" ProductID="11" Quantity="12"/>
<OrderDetail OrderID="10248" ProductID="42" Quantity="10">
<ReturnDetail ReturnOrderID="1111" ReturnDate="1996-08-04T00:00:00"/>
</OrderDetail>
</Order>
</Customer>
<Customer CustomerID="LILAS" ContactName="Carlos Gonzlez">
<Order CustomerID="LILAS" EmployeeID="3" OrderDate="1996-08-16T00:00:00">
<OrderDetail OrderID="10283" ProductID="72" Quantity="3"/>
</Order>
</Customer>
</ROOT>';
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc;
-- SELECT stmt using OPENXML rowset provider
SELECT *
FROM OPENXML (#idoc, '/ROOT/Customer/Order/OrderDetail/ReturnDetail',2)
WITH ( OrderID int '../#OrderID',
order_CustomerID varchar(10) '../../#CustomerID',
EmployeeID varchar(10) '../../#EmployeeID',
ContactName varchar(100) '../../../#ContactName',
CustomerID varchar(10) '../../#CustomerID',
OrderDate datetime '../../#OrderDate',
ProdID int '../#ProductID',
Qty int '../#Quantity',
ReturnOrderID int '#ReturnOrderID',
ReturnDate datetime '#ReturnDate'
);
How do I get all 3 records returned like below
You should use the built-in, native XQuery support (instead of the legacy OPENXML stuff...).
Use this code to get all the details down to the <OrderDetail> node (define your #doc variable as XML):
DECLARE #doc XML;
SET #doc = '......';
SELECT
OrderID = XOD.value('#OrderID', 'int'),
CustomerID = XCus.value('#CustomerID', 'varchar(20)'),
ContactName = XCus.value('#ContactName', 'varchar(50)'),
EmployeeID = XOrder.value('#EmployeeID', 'int'),
OrderDate = XOrder.value('#OrderDate', 'datetime'),
ProductID = XOD.value('#ProductID', 'int'),
Quantity = XOD.value('#Quantity', 'int'),
ReturnOrderID = RetD.value('#ReturnOrderID', 'int'),
ReturnDate = RetD.value('#ReturnDate', 'datetime')
FROM
#doc.nodes('/ROOT/Customer') AS XT(XCus)
CROSS APPLY
XCus.nodes('Order') AS XT2(XOrder)
CROSS APPLY
XOrder.nodes('OrderDetail') AS XT3(XOD)
OUTER APPLY
XOD.nodes('ReturnDetail') AS XT4(RetD)
For each "level" that might contain multiple nodes (like <Customer> under <ROOT> etc.), you need to use CROSS APPLY and the .nodes() XQuery function to get all the child nodes - not just one (first or arbitrary).

XQuery modify with variable

I have the follow XML
<root>
<business name="LM" id="1" total_pes="0">
</business>
<business name="KO" id="354" total_pes="0">
</business>
<business name="TUI" id="889" total_pes="0">
</business>
</root>
I want to update the total_pes attribute with a record count of other table tbl_logs:
id | log
1 | A
1 | A
1 | A
354 | A
354 | A
889 | A
My output XML would be this:
<root>
<business name="LM" id="1" total_pes="3">
</business>
<business name="KO" id="354" total_pes="2">
</business>
<business name="TUI" id="889" total_pes="1">
</business>
</root>
This is what I already done:
DECLARE #total_pes_new int
DECLARE #ID INT
SET #ID = (SELECT TOP 1 ID FROM #IDS)
WHILE #ID IS NOT NULL
BEGIN
set #total_pes_new = ( SELECT COUNT(A.PES) FROM TBL A
WHERE A.ID = #ID)
SET #XML.modify('replace value of (/root/business[#id=sql:variable("#ID")]/#total_pes)[1] with sql:variable("#total_pes_new")')
SET #ID = (SELECT TOP 1 ID FROM #IDS WHERE ID > #ID)
END
I will have problems with this loop. Can anyone help me to do a better solution?
Tks
If your XML is in an XML database, then you also have the option of using XQuery Update, see http://www.w3.org/TR/xquery-update-10/.

How to avoid namespace in child nodes using FOR XML PATH?

I want to create a sitemap xml file (including images) directly from the database without another process (like transformation or another trick).
My query is:
;WITH XMLNAMESPACES(
DEFAULT 'http://www.sitemaps.org/schemas/sitemap/0.9',
'http://www.google.com/schemas/sitemap-image/1.1' as [image] )
SELECT
(SELECT
'mysite' as [loc],
(select
'anotherloc'
as [image:loc]
for XML path('image:image'), type
)
for xml path('url'), type
)
for xml path('urlset'), type
Returns:
<urlset xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<loc>mysite</loc>
<image:image xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<image:loc>anotherloc</image:loc>
</image:image>
</url>
</urlset>
But I need this output, without repeated namespace declaration:
<urlset xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>mysite</loc>
<image:image>
<image:loc>anotherloc</image:loc>
</image:image>
</url>
</urlset>
I'm sure you realise that the additional otiose namespace declarations don't change the meaning of the XML document, so if the result is going to be consumed by an XML-conformant tool, they shouldn't matter. Nevertheless I know there are some tools out there which don't do XML Namespaces correctly, and in a large XML instance superfluous repeated namespace declarations can bloat the size of the result significantly, which may cause its own problems.
In general there is no getting around the fact that each SELECT...FOR XML statement within the scope of a WITH XMLNAMESPACES prefix will generate namespace declarations on the outermost XML element(s) in its result set, in all XML-supporting versions of SQL Server up to SQL Server 2012.
In your specific example, you can get fairly close to the desired XML by separating the SELECTs rather than nesting them, and using the ROOT syntax for the enveloping root element, thus:
DECLARE #inner XML;
WITH XMLNAMESPACES('http://www.google.com/schemas/sitemap-image/1.1' as [image])
SELECT #inner =
(
SELECT
'anotherloc' AS [image:loc]
FOR XML PATH('image:image'), TYPE
)
;WITH XMLNAMESPACES(
DEFAULT 'http://www.sitemaps.org/schemas/sitemap/0.9'
)
SELECT
'mysite' AS [loc],
#inner
FOR XML PATH('url'), ROOT('urlset'), TYPE
The result being:
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>mysite</loc>
<image:image xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns="">
<image:loc>anotherloc</image:loc>
</image:image>
</url>
</urlset>
But this approach doesn't provide a completely general solution to the problem.
You can use UDF. Example:
ALTER FUNCTION [dbo].[udf_get_child_section] (
#serviceHeaderId INT
)
RETURNS XML
BEGIN
DECLARE #result XML;
SELECT #result =
(
SELECT 1 AS 'ChildElement'
FOR XML PATH('Child')
)
RETURN #result
END
GO
DECLARE #Ids TABLE
(
ID int
)
INSERT INTO #Ids
SELECT 1 AS ID
UNION ALL
SELECT 2 AS ID
;WITH XMLNAMESPACES (DEFAULT 'http://www...com/content')
SELECT
[dbo].[udf_get_child_section](ID)
FROM
#Ids
FOR XML PATH('Parent')
Result:
<Parent xmlns="http://www...com/content">
<Child xmlns="">
<ChildElement>1</ChildElement>
</Child>
</Parent>
<Parent xmlns="http://www...com/content">
<Child xmlns="">
<ChildElement>1</ChildElement>
</Child>
</Parent>
Maybe too late for answer, but this is a quick solution.
`DECLARE #PageNumber Int = 1;
DECLARE #siteMapXml XML ;
;WITH XMLNAMESPACES (
'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd' as "schemaLocation",
'http://www.w3.org/2001/XMLSchema-instance' as xsi,
'http://www.google.com/schemas/sitemap-image/1.1' as [image],
DEFAULT 'http://www.sitemaps.org/schemas/sitemap/0.9'
)
SELECT #siteMapXml = (
SELECT
Slug loc,
convert(varchar(300),[Image]) as [image:image/image:loc]
,
Convert(char(10), UpdatedOnUtc, 126) as lastmod,
'hourly' as changefreq,
'0.5' as priority
FROM Products(NOLOCK)
WHERE Pagenumber = #PageNumber
FOR XML PATH ('url'), ROOT ('urlset'))
SELECT #siteMapXml = REPLACE(CAST(#siteMapXml AS NVARCHAR(MAX)), ' xmlns:schemaLocation=', ' xsi:schemaLocation=')
SELECT #siteMapXml`

Generate XML from Table

I have a table like this:
ID Name Column1 Column2 Column3
1 ABC 202.2 1500 34000
2 IJK 104 10000 27000
I want to generate XML like this:
<doc>
<record ID="1" Name="ABC" Column1="202.2" Column2="15000" Column3="34000" />
<record ID="2" Name="IJK" Column1="104" Column2="10000" Column3="27000" />
</doc>
I have got some clue from this forum post and used this code:
CREATE TABLE #tmp (column1 VARCHAR(20), column2 VARCHAR(20), column3 VARCHAR(20))
INSERT INTO #tmp VALUES ( 'data1', 'data2', 'data3' )
INSERT INTO #tmp VALUES ( 'data11', 'data21', 'data31' )
-- FOR XML PATH with ELEMENTS will automatically unpivot the data for you
-- Then reshape your XML using nested FLWOR loops
SELECT
(
SELECT *
FROM #tmp t
FOR XML PATH, ELEMENTS, TYPE
).query('
for $e in row
return
<row>{
for $f in $e/*
return <field name="{local-name($f)}">{data($f)}</field>
}
</row>
')
I tried the following modified version:
SELECT (SELECT * FROM cte_temp t FOR XML PATH, ELEMENTS, TYPE)
.query('for $e in row return
<doc>
{
for $f in $e return <record {local-name($f)}="{data($f)}" />
}
</doc>')
But I'm getting error:
XQuery [query()]: Invalid source character 0x7b found in an identifier
near 'return'.
Why you trying to get fancy.
Select * from #tmp as record
FOR XML AUTO, root('doc')