I have an XML column in a table. This column is called UserDef. The Xml looks like this:
<UserDefs>
<UserDef id="EmpNum">002</UserDef>
<UserDef id="EmpDept">AUT</UserDef>
<UserDef id="EmpName">XYZ ABC</UserDef>
<UserDef id="EmpHireDate">2009-11-01T23:59:00-06:00</UserDef>
</UserDefs>
What should the query look like to return a result like this:
Column1 Column2
--------------------
EmpNum 002
EmpDept AUT
EmpName XYZ ABC
EmpHireDate 2009-11-01 23:59:00
Thank you.
declare #xml xml
set #xml = '<UserDefs>
<UserDef id="EmpNum">002</UserDef>
<UserDef id="EmpDept">AUT</UserDef>
<UserDef id="EmpName">XYZ ABC</UserDef>
<UserDef id="EmpHireDate">2009-11-01T23:59:00-06:00</UserDef>
</UserDefs>'
select R.nref.value('./#id[1]','nvarchar(200)') as Column1,
R.nref.value('./text()[1]','nvarchar(200)') as Column2
from #xml.nodes('/UserDefs/*') R(nref);
consider to use proper length for varchar/nvarchar type for your real data
and also you will need to convert date value properly
if we need to select from table:
declare #xml xml
set #xml = '<UserDefs>
<UserDef id="EmpNum">002</UserDef>
<UserDef id="EmpDept">AUT</UserDef>
<UserDef id="EmpName">XYZ ABC</UserDef>
<UserDef id="EmpHireDate">2009-11-01T23:59:00-06:00</UserDef>
</UserDefs>'
declare #txml table(UserDef xml)
insert into #txml values (#xml);
select
a.value('./#id[1]','nvarchar(200)') as Column1,
a.value('./text()[1]','nvarchar(200)') as Column2
from #txml
CROSS APPLY UserDef.nodes('/UserDefs/*') AS tbl(a)
Related
I have a string that I need to seperate into values seperated by commas. I have achieved this part with the below REPLACE statement:
declare #mc varchar(200)
declare #mc1 varchar(200)
select #mc = 'FRED&#g4;g;MARY&#g4;g;BILL&#g4;g;TIMOTHY&#g4;g;JOHNATHAN'
select #mc1 = REPLACE(#mc, '&#g4;g;',', ')
The replace returns a string 'FRED, MARY, BILL, TIMOTHY, JOHNATHAN'
I then want to have another variable that will return the first 3 characters of each value before the commas, so the above string would be returned as:
'FRE, MAR, TIM, JOH'
Anyone know how I can achieve this?
Also happy for this to be done directly to the original #mc variable
ON SQL Server 2017+ you can make use of openJson to split the string into manageble segments and then string_agg to assemble the desired result:
declare #mc varchar(100)='FRED&#g4;g;MARY&#g4;g;BILL&#g4;g;TIMOTHY&#g4;g;JOHNATHAN'
select String_Agg(v, ', ')
from (select #mc)x(s)
cross apply (
select Left(j.[value],3) v, Convert(tinyint,j.[key]) Seq
from OpenJson(Concat('["',replace(s,';', '","'),'"]')) j
where Convert(tinyint,j.[key]) % 2 = 0
)j;
Demo Fiddle
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
I do not want to return a Xml element if no child elements exists:
SELECT
(
SELECT
'nl' AS [#Language]
,'test' AS [#Value]
WHERE 1=0
FOR XML PATH('Translation'), ROOT('Translations'), TYPE
)
FOR XML RAW('IngredientStatement'), TYPE
In this case <IngredientStatement /> is returned.
One way to do it is to separate the inner select into a common table expression:
;WITH InnerXmlCte AS
(
SELECT
(
SELECT
'nl' AS [#Language]
,'test' AS [#Value]
WHERE 1=0
FOR XML PATH('Translation'), TYPE
) As Translations
)
SELECT Translations
FROM InnerXmlCte
WHERE Translations IS NOT NULL
FOR XML RAW('IngredientStatement'), TYPE
Another approach was to add an XQuery, filtering for <IngredientStatement> with any content:
SELECT
(
SELECT
(
SELECT
'nl' AS [#Language]
,'test' AS [#Value]
WHERE 1=0
FOR XML PATH('Translation'), ROOT('Translations'), TYPE
)
FOR XML RAW('IngredientStatement'), TYPE
).query('/IngredientStatement[*]')
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 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')