There are numerous examples of inserting record sets into XML, but they tend to be very basic and do not cover my specific requirement. In the following instance, I want to extract some data to XML and within that, I need a collection of nodes to represent a variable number of records. I know it can be done, as I have an example, but I do not understand how it works. I do understand my code to return the dataset might make no sense, but it is only meant to represent an example of what I am try to achieve.
SELECT 'Node' AS [A/B],
(
SELECT x.Item
FROM (
SELECT 'Line1' AS [Item]
FROM (SELECT 1 AS x) x
UNION
SELECT 'Line2' AS [Item]
FROM (SELECT 1 AS x) x
) x
FOR XML PATH(''), ROOT('Lines'), TYPE
)
FROM (SELECT 1 AS x) x
FOR XML PATH('Demo')
This gives me the following:
<Demo>
<A>
<B>Node</B>
</A>
<Lines>
<Item>Line1</Item>
<Item>Line2</Item>
</Lines>
</Demo>
What I want is the following:
<Demo>
<A>
<B>Node</B>
<Lines>
<Item>Line1</Item>
<Item>Line2</Item>
</Lines>
</A>
</Demo>
Can anyone help or point me to the correct answer please?
Making some assumptions on the structure of your source data that you should be able to easily adjust around, the following script gives you the output you are after, even across multiple Node groups:
Query
declare #t table([Node] varchar(10),Line varchar(10));
insert into #t values
('Node1','Line1')
,('Node1','Line2')
,('Node1','Line3')
,('Node1','Line4')
,('Node2','Line1')
,('Node2','Line2')
,('Node2','Line3')
,('Node2','Line4')
;
with n as
(
select distinct [Node]
from #t
)
select n.[Node] as [B]
,(select t.Line as Item
from #t as t
where t.[Node] = n.[Node]
for xml path(''), root('Lines'), type
)
from n
for xml path('A'), root('Demo')
;
Output
<Demo>
<A>
<B>Node1</B>
<Lines>
<Item>Line1</Item>
<Item>Line2</Item>
<Item>Line3</Item>
<Item>Line4</Item>
</Lines>
</A>
<A>
<B>Node2</B>
<Lines>
<Item>Line1</Item>
<Item>Line2</Item>
<Item>Line3</Item>
<Item>Line4</Item>
</Lines>
</A>
</Demo>
You need to place A as the node name of the outer query, and Demo as its root.
SELECT 'Node' AS [B],
(
SELECT x.Item
FROM (
SELECT 'Line1' AS [Item]
UNION
SELECT 'Line2' AS [Item]
) x
FOR XML PATH(''), ROOT('Lines'), TYPE
)
FOR XML PATH('A'), ROOT('Demo'), TYPE
SQL Fiddle
Related
Hi i have the current XML:
<?xml version="1.0" encoding="utf-8"?>
<tcpos-export>
<transactions>
<transaction>
<code>1</code>
<description>MAXI MERCADO SELECTO</description>
</shop>
<code>2</code>
<description>MAÑANITA PANADERIA</description>
</till>
<cashier>
<code>2004</code>
<description>NELSON PANADERIA</description>
<first-name>NELSON PANADERIA</first-name>
</cashier>
<trans-item type="article">
<hash-code>832129</hash-code>
<code>30237</code>
<quantity>1</quantity>
<unit-price>0.50</unit-price>
<total-price>0.50</total-price>
</trans-item>
<payment type="cash">
<code>01</code>
<description>Efectivo</description>
<description-translations />
<amount>0.50</amount>
<prepayment>false</prepayment>
<signature-required>false</signature-required>
<payment-timestamp>18.08.2021 06:51:27</payment-timestamp>
<cash-change>4.50</cash-change>
<cash-given>5.00</cash-given>
</payment>
with this xquery sql 2019:
--procesar articulos del dia de venta
SELECT
trans.value('(shop/description/text())[1]','varchar(100)') tienda,
trans.value('(till/code/text())[1]','varchar(100)') caja,
trans.value('(cashier/code/text())[1]','varchar(100)') cajero,
trans.value('(beginning-timestamp/text())[1]','varchar(100)') fecha,
trans.value('(trans-num/text())[1]','varchar(100)') transaccion,
item.value('(code/text())[1]','varchar(100)') itemcode,
item.value('(description/text())[1]','varchar(100)') description,
item.value('(hash-code/text())[1]','int') hashcode
FROM [dbo].[XmlImport] xi
CROSS APPLY xi.[LoadedXML].nodes('tcpos-export/transactions/transaction') x1(trans)
CROSS APPLY x1.trans.nodes('trans-item[
hash-code/text() and
not( unit-price[contains(text()[1], "-")] ) and
not( taxable-amount[contains(text()[1], "-")] ) and
not( delete-operator-id/text() )
]') x2(item)
CROSS APPLY (VALUES (
item.value('(quantity/text())[1]','numeric(10,3)'),
item.value('(weight/text())[1]','numeric(10,3)'),
item.value('(vat-code/text())[1]','varchar(100)')
) ) v(quantity, weight, vatcode)
ORDER BY hashcode;
im already getting those columns correctly from transactions node example cashier, till. In adition im getting info from node. But, im confused in how i can get info from the next node
<payment type="cash">
<code>01</code>
<description>Efectivo</description>
<description-translations />
i would like to get code and description
i tried the following:
adding this as additional cross apply
CROSS APPLY xi.[LoadedXML].nodes('tcpos-export/transactions/transaction') x3(payments)
CROSS APPLY x3.payments.nodes('payment[
code/text()
]') x4(payment)
and in select at the end:
payment.value('(code/text())[1]','varchar(100)')
but when i run the query it stays running and running, and got freezed. I dont know what else i can do.
Please give me some tips.
thank you
You have two options:
You can just select the values directly in your select
SELECT
trans.value('(shop/description/text())[1]','varchar(100)') tienda,
trans.value('(payment/amount/text())[1]','numeric(18,2)') caja,
-- etc....
FROM [dbo].[XmlImport] xi
-- etc....
Alternatively, you can do another CROSS APPLY nodes. This is what you were trying to do, but it should be x1.trans.nodes('payment') because you want to break out each Payment node from transaction, not from the root XML.
Note that the VALUES clause I added in answer to your previous question was needed only because we needed to refer multiple times to the same node.
SELECT
trans.value('(shop/description/text())[1]','varchar(100)') tienda,
trans.value('(till/code/text())[1]','varchar(100)') caja,
trans.value('(cashier/code/text())[1]','varchar(100)') cajero,
trans.value('(beginning-timestamp/text())[1]','varchar(100)') fecha,
trans.value('(trans-num/text())[1]','varchar(100)') transaccion,
item.value('(code/text())[1]','varchar(100)') itemcode,
item.value('(description/text())[1]','varchar(100)') description,
item.value('(hash-code/text())[1]','int') hashcode
payment.value('(code/text())[1]','int') paymentCode
FROM [dbo].[XmlImport] xi
CROSS APPLY xi.[LoadedXML].nodes('tcpos-export/transactions/transaction') x1(trans)
CROSS APPLY x1.trans.nodes('trans-item[
hash-code/text() and
not( unit-price[contains(text()[1], "-")] ) and
not( taxable-amount[contains(text()[1], "-")] ) and
not( delete-operator-id/text() )
]') x2(item)
CROSS APPLY x1.trans.nodes('payment') x3(payment)
ORDER BY hashcode;
Note that if there might be no payment node then you need OUTER APPLY not CROSS APPLY
I am setting up alert using extended event in which I am pulling out info in XML format so I got stuck in finding out the values - Object name from this XML.
SELECT CAST(data AS XML) AS [result]
FROM #temp
WHERE data LIKE '%<text>Abort</text>%'
Using this query, I have pulled out those records which gets time out in XML format and through this xml, we need to pull XYZ value as object name using T-SQL <value>XYZ</value></data>
Output of above select query:
<event name="rpc_completed" package="sqlserver" timestamp="2019-02-20T14:42:39.678Z"><data name="cpu_time"><value>15000</value></data><data name="duration"><value>29999325</value></data><data name="physical_reads"><value>0</value></data><data name="logical_reads"><value>363</value></data><data name="writes"><value>0</value></data><data name="result"><value>2</value><text>Abort</text></data><data name="row_count"><value>9</value></data><data name="connection_reset_option"><value>0</value><text>None</text></data><data name="object_name"><value>XYZ</value></data><data name="statement"><value>exec XYZ </value></data><data name="data_stream"><value /></data><data name="output_parameters"><value /></data><action name="transaction_id" package="sqlserver"><value>0</value></action><action name="session_id" package="sqlserver"><value>1381</value></action><action name="server_principal_name" package="sqlserver"><value>sq</value></action><action name="database_name" package="sqlserver"><value>PR</value></action><action name="database_id" package="sqlserver"><value>5</value></action><action name="client_pid" package="sqlserver"><value>32048</value></action><action name="client_hostname" package="sqlserver"><value>RuntimeHost</value></action><action name="client_app_name" package="sqlserver"><value>test</value></action><action name="event_sequence" package="package0"><value>133050</value></action></event>
NA
Output should be like this:
Object Name
XYZ
You can use nodes to filter the items inside your xml by attribute value and then value to extract the data you need:
;with x as(
SELECT CAST(data AS XML) AS [result]
FROM #temp
WHERE data LIKE '%<text>Abort</text>%'
)
select
t.s.value('.', 'nvarchar(max)') as object_name
from
x
cross apply
[result].nodes('//data[#name = "object_name"]/value') t(s)
Result:
Edit
One approach to retrieve database_name is adding another nodes filtering on action tags. To get the timestamp you can just add a value in the select clause specifying the correct xpath expression:
;with x as(
SELECT CAST(data AS XML) AS [result]
FROM #temp
WHERE data LIKE '%<text>Abort</text>%'
)
select
t.s.value('.', 'nvarchar(max)') as [object_name]
, u.s.value('.', 'nvarchar(max)') as [database_name]
, [result].value('(/event/#timestamp)[1]', 'nvarchar(max)') as [timestamp]
from
x
cross apply
[result].nodes('//data[#name = "object_name"]/value') t(s)
cross apply
[result].nodes('//action[#name = "database_name"]/value') u(s)
Results with database_name and timestamp:
I have a XML in Source Table. I need to parse this XML to 3 different tables which has Parent Child relationship. I can do this in C# but currently for this i need to implement it at SQL server side.
The sample xml looks like:
<ROWSET>
<ROW>
<HEADER_ID>5001507</HEADER_ID>
<ORDER_NUMBER>42678548</ORDER_NUMBER>
<CUST_PO_NUMBER>LSWQWE1</CUST_PO_NUMBER>
<CUSTOMER_NUMBER>38087</CUSTOMER_NUMBER>
<CUSTOMER_NAME>UNIVERSE SELLER</CUSTOMER_NAME>
<LINE>
<LINE_ROW>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532839</LINE_ID>
<LINE_NUMBER>1</LINE_NUMBER>
<ITEM_NUMBER>STAGEPAS 600I-CA</ITEM_NUMBER>
<ORDER_QUANTITY>5</ORDER_QUANTITY>
</LINE_ROW>
<LINE_ROW>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532901</LINE_ID>
<LINE_NUMBER>3</LINE_NUMBER>
<ITEM_NUMBER>CD-C600 RK</ITEM_NUMBER>
<ORDER_QUANTITY>6</ORDER_QUANTITY>
</LINE_ROW>
<LINE_ROW>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532902</LINE_ID>
<LINE_NUMBER>4</LINE_NUMBER>
<ITEM_NUMBER>CD-S300 RK</ITEM_NUMBER>
<ORDER_QUANTITY>8</ORDER_QUANTITY>
</LINE_ROW>
</LINE>
<PRCADJ>
<PRCADJ_ROW>
<PRICE_ADJUSTMENT_ID>43095064</PRICE_ADJUSTMENT_ID>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532839</LINE_ID>
<ADJUSTED_AMOUNT>-126</ADJUSTED_AMOUNT>
</PRCADJ_ROW>
<PRCADJ_ROW>
<PRICE_ADJUSTMENT_ID>43095068</PRICE_ADJUSTMENT_ID>
<HEADER_ID>5001507</HEADER_ID>
<LINE_ID>12532840</LINE_ID>
<ADJUSTED_AMOUNT>-96.6</ADJUSTED_AMOUNT>
</PRCADJ_ROW>
</PRCADJ>
</ROW>
</ROWSET>
The issue is the Parent can have multiple child and each child can multiple sub child. How can i write query to transfer this into Sql Server 2005
You need to use three CROSS APPLY operators to break up the "list of XML elements" into separate pseudo tables of XML rows, so you can access their properties - something like this:
SELECT
HeaderID = XCRow.value('(HEADER_ID)[1]', 'int'),
OrderNumber = XCRow.value('(ORDER_NUMBER)[1]', 'int'),
LineHeaderID = XCLine.value('(HEADER_ID)[1]', 'int'),
LineID = XCLine.value('(LINE_ID)[1]', 'int'),
LineNumber = XCLine.value('(LINE_NUMBER)[1]', 'int'),
PriceAdjustmentID = XCPrc.value('(PRICE_ADJUSTMENT_ID)[1]', 'int'),
AdjustedAmount = XCPrc.value('(ADJUSTED_AMOUNT)[1]', 'decimal(20,4)')
FROM
dbo.YourTableNameHere
CROSS APPLY
Data.nodes('/ROWSET/ROW') AS XTRow(XCRow)
CROSS APPLY
XCRow.nodes('LINE/LINE_ROW') AS XTLine(XCLine)
CROSS APPLY
XCRow.nodes('PRCADJ/PRCADJ_ROW') AS XTPrc(XCPrc)
With this, the first CROSS APPLY will handle all the elements that are found directly under <ROWSET> / <ROW> (the header information), the second one will enumerate all instances of <LINE> / <LINE_ROW> below that header element, and the third CROSS APPLY handles the <PRCADJ> / <PRCADJ_ROW> elements, also below the header.
You might need to tweak the outputs a bit - and I only picked two or three of the possible values - extend and adapt to your own needs! But this should show you the basic mechanism - the .nodes() method returns a "pseudo table" of XML fragments, one for each match of the XPath expression you define.
you can do some thing like this. using cross apply you will get node elements and then extract the value using value clause. you need to specify the column type i.e int or varchar etc.
The result can then be inserted using insert into select query.
insert into Table1 values ( header_id, order_number, cust_po_number)
select R.value('(HEADER_ID)[1]', 'int') As header_id,
R.value('(ORDER_NUMBER)[1]', 'int') as order_number,
R.value('(CUST_PO_NUMBER)[1]', 'varchar(256)') as cust_po_number
from table
cross apply XMLdata.nodes('/ROWSET/ROW') AS P(R)
insert into Table2 values ( header_id, line_id, line_number)
select R.value('(HEADER_ID)[1]', 'int') As header_id,
R.value('(LINE_ID)[1]', 'int') as line_id,
R.value('(LINE_NUMBER)[1]', 'int') as line_number
from table
cross apply XMLdata.nodes('/ROWSET/ROW/LINE/LINE_ROW') AS P(R)
I need to parse the XML below, however the values generated is wrong. Anyone has an idea?
Results
Code Pay1_515
5570, Industry1, 1 10
5570, Industry2, 2 10
Sample XML
DECLARE #XML xml = '<?xml version="1.0" encoding="utf-8"?>
<CookedData>
<Column DestinationColumnCode="Code">
<r v="5570, Industry1, 1" />
<r v="5570, Industry2, 2" />
</Column>
<Column DestinationColumnCode="Pay1_515">
<r v="10" />
<r v="10" />
</Column>
</CookedData>';
Sample code
with C as
(
select T.X.value('(Column[#DestinationColumnCode = "Code"]/r/#v)[1]', 'NVARCHAR(3000)') as Code,
T.X.value('(Column[#DestinationColumnCode = "Pay1_515"]/r/#v)[1]', 'NVARCHAR(3000)') as Pay
from #XML.nodes('/CookedData/Column') as T(X)
)
SELECT * FROM C
Try this:
with C as
(
select T.X.value('(//Column[#DestinationColumnCode = "Code"]/r/#v)[1]', 'NVARCHAR(3000)') as Code,
T.X.value('(//Column[#DestinationColumnCode = "Pay1_515"]/r/#v)[1]', 'NVARCHAR(3000)') as Pay
from #XML.nodes('/CookedData/Column') as T(X)
You need to use this:
SELECT
Code = T.X.value('#DestinationColumnCode', 'VARCHAR(30)'),
RValue= r.value('#v', 'varchar(50)')
FROM
#XML.nodes('/CookedData/Column') as T(X)
CROSS APPLY
X.nodes('r') AS T2(R)
This will produce the output:
This has been bugging me all morning. I wondered if I could improve on the answer given by #marc_s (to the detriment of my actual work :-/) and I have managed to get the required output. However, some caveats:
This assumes that the order of child r nodes in each Column node correlates to the other (i.e. the first r node in each should be side by side and so on). This is because there is no other way to tie them together that I can see.
It's not very generic - as it stands, it won't work for n columns without extra code and fiddling about.
You don't mind some pretty filthy code which is likely to be pretty poor performing on large amounts of data (even with some tuning - my brain has now melted and can't cope with any more fiddling on it).
I've tested with a few more rows in the XML but I would not guarantee it's efficacy in all cases, it would need some serious testing.
I couldn't be bothered to get the final column names right.
Someone, anyone, please feel free to improve this, but FWIW here it is (complete, working code):
DECLARE #XML xml = '<?xml version="1.0" encoding="utf-8"?>
<CookedData>
<Column DestinationColumnCode="Code">
<r v="5570, Industry1, 1" />
<r v="5570, Industry2, 2" />
</Column>
<Column DestinationColumnCode="Pay1_515">
<r v="10" />
<r v="10" />
</Column>
</CookedData>';
DECLARE #shredXML TABLE (Code VARCHAR(10), RValue VARCHAR(20))
DECLARE #Codes TABLE (RowNum INT, ColCode VARCHAR(10))
--Do this once at the start and use the results.
INSERT INTO #shredXML
SELECT
Code = T.X.value('#DestinationColumnCode', 'VARCHAR(30)'),
RValue= r.value('#v', 'varchar(50)')
FROM
#XML.nodes('/CookedData/Column') as T(X)
CROSS APPLY
X.nodes('r') AS T2(R)
--First get the distinct list of DestinationColumnCode values.
INSERT INTO #Codes
SELECT ROW_NUMBER() OVER (ORDER BY colcodes.Code), colcodes.Code
FROM #shredXML colcodes
GROUP BY colcodes.Code
SELECT p1.RValue, p2.RValue
FROM (
--Get all the values for the code column
SELECT ROW_NUMBER() OVER (ORDER BY Codes.RValue) AS RowNum, Codes.RValue, Codes.Code
FROM (
SELECT x.Code, x.RValue FROM #shredXML x
INNER JOIN #Codes c
ON c.ColCode = x.Code
WHERE c.RowNum = 1) AS Codes) AS p1
--Join the values column on RowNum
INNER JOIN (
SELECT ROW_NUMBER() OVER (ORDER BY Vals.RValue) AS RowNum, Vals.RValue, Vals.Code
FROM (
SELECT x.Code, x.RValue FROM #shredXML x
INNER JOIN #Codes c
ON c.ColCode = x.Code
WHERE c.RowNum = 2) AS Vals) AS p2
ON p1.RowNum = p2.RowNum
EDIT
Finally got SQLFiddle to play ball and run the example above
I'm trying to SELECT from a table containing XML column. I would like to obtain specific node and have a row created for each one.
The XML is directly obtained from Reporting Services database and contains RDL (report) structure. My goal is to display all ‹Textbox›‹Value›example‹/Value›‹/Textbox› values for each report. The location of ‹Textbox› nodes is unpredictable (it can be part of any element somewhere in XML structure).
Below is the current code, but for some reason id doesn't work:
IF object_id('tempdb..#c') IS NOT NULL
DROP TABLE #c
select top 50
path as reportpath
,name as reportname
,convert(xml, convert(varbinary(max), content)) as reportxml
into
#c
from
reportserver.dbo.catalog
where
content is not null
order by creationdate desc
-----------------------------------------
DECLARE #x XML
SELECT #x =
( SELECT
[reportpath]
,[reportname]
,[reportxml].query('
for $a in //../Textbox
return ‹Textbox
valueX="{$a/Value}"
/›
')
FROM #c AS reports
FOR XML AUTO
)
select #x
-----------------------------------------
SELECT [reportpath] = T.Item.value('../#reportpath', 'nvarchar(max)'),
[reportname] = T.Item.value('../#reportname', 'nvarchar(max)'),
value = T.Item.value('#value' , 'nvarchar(max)')
FROM #x.nodes('//reports/Textbox') AS T(Item)
Example below shows sample "Textbox" containg a "Value":
‹RowGrouping›
‹Width›2.53968cm‹/Width›
‹DynamicRows›
‹Grouping Name="matrix1_OperationalWeek2"›
‹GroupExpressions›
‹GroupExpression›=Fields!OperationalWeek.Value‹/GroupExpression›
‹/GroupExpressions›
‹/Grouping›
‹ReportItems›
‹Textbox Name="textbox35"›
‹rd:DefaultName›textbox35‹/rd:DefaultName›
‹Style›
‹BackgroundColor›White‹/BackgroundColor›
‹PaddingLeft›2pt‹/PaddingLeft›
‹PaddingRight›2pt‹/PaddingRight›
‹PaddingTop›2pt‹/PaddingTop›
‹PaddingBottom›2pt‹/PaddingBottom›
‹/Style›
‹ZIndex›8‹/ZIndex›
‹Value›=Fields!OperationalWeek.Value‹/Value›
‹/Textbox›
‹/ReportItems›
‹/DynamicRows›
‹/RowGrouping›
PS I had some trouble with stackoverflow code formatting so I replaced < and > marks with ‹ and ›. Sorry about that.
Based on Bret's Blog ([http://blogs.netconnex.com/2011/05/extracting-ssrs-report-rdl-xml-from.html][1])
and adding the namespace gets you results... I wish I could claim I understand well enough to explain but I mostly find my way by "stumbling" through it.
--================================================
;WITH XMLNAMESPACES (
DEFAULT 'http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition',
'http://schemas.microsoft.com/SQLServer/reporting/reportdesigner' AS rd --ReportDefinition
)
select top 50
c.Path as reportpath
--, c.name as reportname
,t.value('#Name','VARCHAR(100)') as TextboxName
,t.value('data(Paragraphs/Paragraph/TextRuns/TextRun/Value)[1]', 'varchar(max)') as value
from
reportserver.dbo.catalog c
cross apply
(select convert(xml, convert(varbinary(max), content))) as R(reportxml)
cross apply
--Get all the Query elements (The "*:" ignores any xml namespaces)
r.reportxml.nodes('//*:Textbox') n(t)
where
content is not null
and c.Type = 2 -- Reports
order by creationdate desc
This simple XQuery:
for $a in //Textbox
return
<Textbox
valueX="{$a/Value}"
/>
when applied on the provided XML document (namespace definition added to make it well-formed):
<RowGrouping xmlns:rd="rd">
<Width>2.53968cm</Width>
<DynamicRows>
<Grouping Name="matrix1_OperationalWeek2">
<GroupExpressions>
<GroupExpression>=Fields!OperationalWeek.Value</GroupExpression>
</GroupExpressions>
</Grouping>
<ReportItems>
<Textbox Name="textbox35">
<rd:DefaultName>textbox35</rd:DefaultName>
<Style>
<BackgroundColor>White</BackgroundColor>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
<ZIndex>8</ZIndex>
<Value>=Fields!OperationalWeek.Value</Value>
</Textbox>
</ReportItems>
</DynamicRows>
</RowGrouping>
produces the wanted, correct result:
<?xml version="1.0" encoding="UTF-8"?>
<Textbox valueX="=Fields!OperationalWeek.Value"/>
Therefore, if you cannot get result, your problem is in something else, not in the XQuery code.
I can't test if this works but it should do what you want.
select top 50
path as reportpath
,name as reportname
,n.t.value('Value[1]', 'varchar(max)') as value
from
reportserver.dbo.catalog
cross apply
(select convert(xml, convert(varbinary(max), content))) as c(reportxml)
cross apply
c.reportxml.nodes('//Textbox') n(t)
where
content is not null
order by creationdate desc