how would you write a t-sql query if the namespace is prefixed in all of the elements? I've tried many variations, and this is what I've got so far, but it just doesn't work...
DECLARE #x xml
SET #x = (SELECT xml_data_column FROM dbo.Table
WHERE xmlFileName = 'E:\trnFile.xml' )
;WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS [soap]
, 'tns:RetrievePurchaseResponse xmlns:tns="urn:Transaction"' AS tns)
SELECT t.c.value('orderRef[1]', 'int') orderReference
, t.c.value('orderNumber[1]', 'varchar(100)') orderNumber
, t.c.value('subtotal[1]', 'varchar(100)') subtotal
FROM #x.nodes('/soap:Envelope/soap:Body/tns:RetrievePurchaseResponse/tns:purchase') AS t(c)
Assistance will be greatly appreciated !
Here is the XML input file:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<tns:RetrievePurchaseResponse xmlns:tns="urn:Transaction">
<tns:purchase>
<tns:orderRef>10027</tns:orderRef>
<tns:orderNumber>425816</tns:orderNumber>
<tns:subtotal>95.00</tns:subtotal>
</tns:purchase>
</tns:RetrievePurchaseResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Expected output would be
orderReference, orderNumber, orderUserEmail
10027, 425816, user#domain.com
DECLARE #x xml
SET #x = (
SELECT xml_data_column
FROM dbo.Table
WHERE xmlFileName = 'E:\trnFile.xml'
);
WITH XMLNAMESPACES(
'urn:Transaction' AS tns
)
SELECT
t.c.value('tns:orderRef[1]', 'int') orderReference
, t.c.value('tns:orderNumber[1]', 'varchar(100)') orderNumber
, t.c.value('tns:subtotal[1]', 'varchar(100)') subtotal
FROM
#x.nodes('//tns:RetrievePurchaseResponse/tns:purchase') AS t(c)
declare #x xml
set #x = '
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<tns:RetrievePurchaseResponse xmlns:tns="urn:Transaction">
<tns:purchase>
<tns:orderRef>10027</tns:orderRef>
<tns:orderNumber>425816</tns:orderNumber>
<tns:subtotal>95.00</tns:subtotal>
</tns:purchase>
</tns:RetrievePurchaseResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
with xmlnamespaces('urn:Transaction' as tns)
select
#X.value('(//tns:RetrievePurchaseResponse/tns:purchase/tns:orderRef)[1]', 'int') orderReference,
#x.value('(//tns:RetrievePurchaseResponse/tns:purchase/tns:orderNumber)[1]', 'int') orderNumber,
#x.value('(//tns:RetrievePurchaseResponse/tns:purchase/tns:orderUserEmail)[1]', 'varchar(100)') orderUserEmail
Related
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).
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/.
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`
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')
declare #myDoc xml
set #myDoc = '<Form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.mydomain.org/MySchema.xsd" SectionId="ABCD" Description="Some stuff">
<ProductDescription ProductID="1" ProductName="Road Bike">
<Features>
<Warranty>1 year parts and labor</Warranty>
<Maintenance>3 year parts and labor extended maintenance is available</Maintenance>
</Features>
</ProductDescription>
</Form>'
;WITH XMLNAMESPACES( 'http://www.w3.org/2001/XMLSchema-instance' as xsi, 'http://www.w3.org/2001/XMLSchema' as xsd, DEFAULT 'http://www.mydomain.org/MySchema.xsd' )
SELECT #myDoc.value('/Form[#SectionId][0]', 'varchar')
I need to obtain the attribute value of SectionId as a nvarchar ? how do I do it ?...
T and R
Mark
You could write it even simpler:
;WITH XMLNAMESPACES(DEFAULT 'http://www.mydomain.org/MySchema.xsd')
SELECT #myDoc.value('(/Form/#SectionId)[1]', 'VARCHAR(100)') AS SectionId
Since you're never using/referring to any of the xsi or xsd namespaces, there's no need to declare those.
And since you're only fetching one attribute from one element, there's really no point in using the .nodes() function to create an internal "dummy table", either.
;WITH XMLNAMESPACES( 'http://www.w3.org/2001/XMLSchema-instance' as xsi, 'http://www.w3.org/2001/XMLSchema' as xsd, DEFAULT 'http://www.mydomain.org/MySchema.xsd' )
SELECT Node.value('#SectionId', 'VARCHAR(100)') AS SectionId
FROM #myDoc.nodes('/Form') TempXML (Node);