Is there any way to find out specific tag values from XML? - sql

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:

Related

SQL Server XML - embedding a dataset within an existing node

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

Wrapping an xml document loses utf coding

In Sql Server I'm using
select * from [Database].[dbo].[Table] for xml path ('whatever')
which gives me an xml output with, importantly, newline 
 notifiers on any entries that have the new lines.
I need this output wrapped in a few more xml formatting elements but using
select '<?xml version="1.0"?><root><whateverses>'
+ (select * from [Database].[dbo].[Table] for xml path ('whatever'))
+ '</whateverses></root>';
returns just a string, with the newlne notifiers MISSING.
How can I preserve these? How do I wrap my xml in a few extras while keeping the output as an xml?
By trying to wrap the XML in varchar/string you're implicitly converting the XML to varchar/string. If you want to embed the XML in other tags try something like the following:
select *
into [dbo].[Foo]
from (values ('Hello', 'World') ) Src ([Bar], [Baz]);
select *
from [dbo].[Foo]
for xml path ('whatever');
select (
select *
from [dbo].[Foo]
for xml path ('whatever'), type
)
for xml path('whateverses'), root('root');
Which yields the XML results:
<whatever>
<Bar>Hello</Bar>
<Baz>World</Baz>
</whatever>
and:
<root>
<whateverses>
<whatever>
<Bar>Hello</Bar>
<Baz>World</Baz>
</whatever>
</whateverses>
</root>

Parse saved xml from table MSSQL server

I have a table with a column named xml. Table is text type but contains xml responses. I need 2 values from this column:
PL81300032102 from <ie801:Traderid>
Some Company sp. z o.o. from <ie801:TraderName>.
It is possible in SQL Server using a query?
<?xml version="1.0" encoding="UTF-8"?><EMCSToTrader xmlns="urn:publicid:-:PL:GOV:MF:EMCS:PHASE3:EMCS-TRADER:REQUEST:V1.00" xmlns:ie801="urn:publicid:-:EC:DGTAXUD:EMCS:PHASE3:IE801:V1.51" xmlns:tms="urn:publicid:-:EC:DGTAXUD:EMCS:PHASE3:TMS:V1.51" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Message><ie801:IE801>
<ie801:Header>
<tms:MessageSender>NDEA.PL</tms:MessageSender>
<tms:MessageRecipient>PL61300032004</tms:MessageRecipient>
<tms:DateOfPreparation>2018-07-17</tms:DateOfPreparation>
<tms:TimeOfPreparation>11:16:44.631</tms:TimeOfPreparation>
<tms:MessageIdentifier>PL#IE801#69474394</tms:MessageIdentifier>
</ie801:Header>
<ie801:Body>
<ie801:EADContainer>
<ie801:ConsigneeTrader language="pl">
<ie801:Traderid>PL81300032102</ie801:Traderid>
<ie801:TraderName>Some Company sp. z o.o.</ie801:TraderName> <...>
Table structure:
I was able to convert text data to xml type using:
SELECT TOP (10) * FROM (
SELECT CAST([xml] AS XML) AS xmlcontent
FROM [emcskomunikaty]
) det
Now trying to get value from xml.
I suppose you can do this:
SELECT
xmldata.value('declare namespace ns1="urn:publicid:-:EC:DGTAXUD:EMCS:PHASE3:IE801:V1.51"; (//ns1:Traderid)[1]', 'VARCHAR(100)') AS Traderid,
xmldata.value('declare namespace ns1="urn:publicid:-:EC:DGTAXUD:EMCS:PHASE3:IE801:V1.51"; (//ns1:TraderName)[1]', 'VARCHAR(100)') AS TraderName
FROM #t
CROSS APPLY (SELECT CAST(xml AS XML)) AS CA(xmldata)
The only tricky part here is handling the namespaces. If you choose ignore namespaces then just use //*:Traderid.

Passing irregular xml file to the stored procedure

I have a sample xml as follows. I am trying to get all or specific data and then insert into the my sql table which has the same columns representing the values coming from xml. I looked through some solutions but the xml files are not formatted like I have in here. Can you help me?
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<VehicleStatusResponse xmlns:ns2=
"http://fms-standard.com/rfms/v1.0.0/xsd/common/position" xmlns="http://fms-standard.com/rfms/v1.0.0/xsd/status">
<VehicleStatus>
<VIN>VF254ANA735752628</VIN>
<TriggerType>TIMER</TriggerType>
<CreatedDateTime>2014-09-08T09:30:20</CreatedDateTime>
<ReceivedDateTime>2014-09-08T09:30:57</ReceivedDateTime>
<GNSSPosition>
<ns2:Latitude>49.18557</ns2:Latitude>
<ns2:Longitude>11.18557</ns2:Longitude>
<ns2:Heading>33</ns2:Heading>
<ns2:Altitude>500</ns2:Altitude>
<ns2:Speed>16.4</ns2:Speed>
<ns2:PositionDateTime>2014-09-08T09:30:20</ns2:PositionDateTime>
</GNSSPosition>
<WheelBasedSpeed>16.07</WheelBasedSpeed>
<TachographSpeed>15.83</TachographSpeed>
<HRTotalVehicleDistance>817.5</HRTotalVehicleDistance>
<EngineTotalFuelUsed>575</EngineTotalFuelUsed>
<FuelLevel1>83</FuelLevel1>
<CatalystFuelLevel>88.48</CatalystFuelLevel>
<GrossCombinationVehicleWeight>10000</GrossCombinationVehicleWeight>
</VehicleStatus>
</VehicleStatusResponse>
You can use an XML type and XML methods if you remove or modify the declaration. The SQL Server XML type only supports UCS-2 encoding and doesn't recognize "standalone". The example below uses string manipulation to tweak the declaration. You'll need to change the data types according to your actual column types and should specify an explicit column list on the INSERT statement. I omitted that in this example only because I didn't want to assume your actual table columns matched the element names in the XML.
DECLARE #xml xml;
DECLARE #xmlString nvarchar(MAX) = N'<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<VehicleStatusResponse xmlns:ns2=
"http://fms-standard.com/rfms/v1.0.0/xsd/common/position" xmlns="http://fms-standard.com/rfms/v1.0.0/xsd/status">
<VehicleStatus>
<VIN>VF254ANA735752628</VIN>
<TriggerType>TIMER</TriggerType>
<CreatedDateTime>2014-09-08T09:30:20</CreatedDateTime>
<ReceivedDateTime>2014-09-08T09:30:57</ReceivedDateTime>
<GNSSPosition>
<ns2:Latitude>49.18557</ns2:Latitude>
<ns2:Longitude>11.18557</ns2:Longitude>
<ns2:Heading>33</ns2:Heading>
<ns2:Altitude>500</ns2:Altitude>
<ns2:Speed>16.4</ns2:Speed>
<ns2:PositionDateTime>2014-09-08T09:30:20</ns2:PositionDateTime>
</GNSSPosition>
<WheelBasedSpeed>16.07</WheelBasedSpeed>
<TachographSpeed>15.83</TachographSpeed>
<HRTotalVehicleDistance>817.5</HRTotalVehicleDistance>
<EngineTotalFuelUsed>575</EngineTotalFuelUsed>
<FuelLevel1>83</FuelLevel1>
<CatalystFuelLevel>88.48</CatalystFuelLevel>
<GrossCombinationVehicleWeight>10000</GrossCombinationVehicleWeight>
</VehicleStatus>
</VehicleStatusResponse>';
SET #xmlString = REPLACE(#xmlString, 'encoding="UTF-8"', 'encoding="UCS-2"');
SET #xmlString = REPLACE(#xmlString, 'standalone="true"', '');
SET #xml = #xmlString;
WITH XMLNAMESPACES (
DEFAULT 'http://fms-standard.com/rfms/v1.0.0/xsd/status'
,'http://fms-standard.com/rfms/v1.0.0/xsd/common/position' AS ns2
)
INSERT INTO dbo.YourTable
SELECT
#xml.value('(/VehicleStatusResponse/VehicleStatus/VIN)[1]', 'varchar(50)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/TriggerType)[1]', 'varchar(50)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/CreatedDateTime)[1]', 'datetime2(3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/ReceivedDateTime)[1]', 'datetime2(3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Latitude)[1]', 'decimal(8,5)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Longitude)[1]', 'decimal(8,5)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Heading)[1]', 'int')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Altitude)[1]', 'int')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:Speed)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GNSSPosition/ns2:PositionDateTime)[1]', 'datetime2(3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/WheelBasedSpeed)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/TachographSpeed)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/HRTotalVehicleDistance)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/EngineTotalFuelUsed)[1]', 'int')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/CatalystFuelLevel)[1]', 'decimal(8,3)')
, #xml.value('(/VehicleStatusResponse/VehicleStatus/GrossCombinationVehicleWeight)[1]', 'int');
First of all you need to get your value into a declare variable of type XML or into an XML-typed data table column. As your XML contains namespaces you have to declare them in a WITH XMLNAMESPACES first. You might use wildcard syntax (*:), but its better to be as specific as possible.
The .nodes() call navigates to the Level of <VehicleStatus>. All elements below are simply 1:1 and easy to read...
You can try it like this:
DECLARE #xml XML=
N'<VehicleStatusResponse xmlns:ns2=
"http://fms-standard.com/rfms/v1.0.0/xsd/common/position" xmlns="http://fms-standard.com/rfms/v1.0.0/xsd/status">
<VehicleStatus>
<VIN>VF254ANA735752628</VIN>
<TriggerType>TIMER</TriggerType>
<CreatedDateTime>2014-09-08T09:30:20</CreatedDateTime>
<ReceivedDateTime>2014-09-08T09:30:57</ReceivedDateTime>
<GNSSPosition>
<ns2:Latitude>49.18557</ns2:Latitude>
<ns2:Longitude>11.18557</ns2:Longitude>
<ns2:Heading>33</ns2:Heading>
<ns2:Altitude>500</ns2:Altitude>
<ns2:Speed>16.4</ns2:Speed>
<ns2:PositionDateTime>2014-09-08T09:30:20</ns2:PositionDateTime>
</GNSSPosition>
<WheelBasedSpeed>16.07</WheelBasedSpeed>
<TachographSpeed>15.83</TachographSpeed>
<HRTotalVehicleDistance>817.5</HRTotalVehicleDistance>
<EngineTotalFuelUsed>575</EngineTotalFuelUsed>
<FuelLevel1>83</FuelLevel1>
<CatalystFuelLevel>88.48</CatalystFuelLevel>
<GrossCombinationVehicleWeight>10000</GrossCombinationVehicleWeight>
</VehicleStatus>
</VehicleStatusResponse>';
--This is the query
WITH XMLNAMESPACES(DEFAULT 'http://fms-standard.com/rfms/v1.0.0/xsd/status'
,'http://fms-standard.com/rfms/v1.0.0/xsd/common/position' AS ns2)
SELECT vs.value('VIN[1]','nvarchar(max)') AS VehicleStatus_VIN
,vs.value('TriggerType[1]','nvarchar(max)') AS VehicleStatus_TriggerType
,vs.value('CreatedDateTime[1]','datetime') AS VehicleStatus_CreatedDateTime
,vs.value('ReceivedDateTime[1]','datetime') AS VehicleStatus_ReceivedDateTime
,vs.value('(GNSSPosition/ns2:Latitude)[1]','decimal(14,6)') AS VehicleStatus_GNSSPosition_Latitude
,vs.value('(GNSSPosition/ns2:Longitude)[1]','decimal(14,6)') AS VehicleStatus_GNSSPosition_Longitude
/*other columns follow the same pattern*/
FROM #xml.nodes('/VehicleStatusResponse/VehicleStatus') AS A(vs)
update: insert into a table
Easiest was to wrap this call as CTE like
WITH XMLNAMESPACES(...)
,DerivedTableCTE AS
(
The query here
)
INSERT INTO YourTable (col1, col2, col3, ...)
SELECT col1, col2, col3, ...
FROM DerivedTableCTE

T-SQL select all XML nodes as rows from RDL data

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