SQL Server stored procedure - Table to XML - sql

I created an XML Object from an SQL table but still need to insert a tag and hardcode a value for each of my columns.
Here is my query and result
SELECT
EmployeeName, RequestStatus
FROM K2.SmartBoxData.Akin_LeaveRequest_Header_SMO
WHERE ID =32
FOR XML PATH ('Message')
<Message>
<EmployeeName>Developer</EmployeeName>
<RequestStatus>Line Manager Approval</RequestStatus>
</Message>
Here is my desired result
<Message>
<tag>
<hardcode> my value </hardcode>
<EmployeeName>Developer</EmployeeName>
</tag>
<tag>
<hardcode> my value 2 </hardcode>
<RequestStatus>Line Manager Approval</RequestStatus>
</tag>
</Message>

You can use nested FOR XML subqueries to do this. Make sure to add ,TYPE to the nested FOR XML otherwise it will try escape it.
Do not specify column names for the subqueries
SELECT
(
SELECT
hardcode = ' my value ',
lrh.EmployeeName
FOR XML PATH('tag'), TYPE
),
(
SELECT
hardcode = ' my value 2 ',
lrh.RequestStatus
FOR XML PATH('tag'), TYPE
)
FROM SmartBoxData.Akin_LeaveRequest_Header_SMO lrh
WHERE ID =32
FOR XML PATH ('Message'), TYPE;
Alternatively specify column names, but an empty PATH
SELECT
tag = (
SELECT
hardcode = ' my value ',
lrh.EmployeeName
FOR XML PATH(''), TYPE
),
tag = (
SELECT
hardcode = ' my value 2 ',
lrh.RequestStatus
FOR XML PATH(''), TYPE
)
FROM SmartBoxData.Akin_LeaveRequest_Header_SMO lrh
WHERE ID =32
FOR XML PATH ('Message'), TYPE;
db<>fiddle

Related

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>

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

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:

How to generate XML using SQL PATH Mode, with Line Items

I am trying to generate an XML string based on data in two SQL tables. One contains Order Header data, the other one Line Item data.
My problem is that I can't get the Line Item info to appear properly as multiple elements within a single order:
This is the SQL statement:
Select
LTRIM(RTRIM(H.CustPONbr)) As "Transactions/Transaction/CustomerOrdNumber",
(
Select LTRIM(RTRIM(InvtID)) As "data()" From X888_Amazon_Order_Line L1
Where L1.CpnyID = H.CpnyID And L1.CustPONbr = H.CustPONbr
FOR XML PATH (''), ELEMENTS
) As "Transactions/Transaction/LineItems/LineItem/InvtId"
From X888_Amazon_Order_Header H (nolock)
where h.CustPONbr = '99999014'
For XML PATH ('ProcessEngineSubmission'), Root ('XML'), ELEMENTS
This is the result that I get:
<XML>
<Transactions>
<Transaction>
<CustomerOrdNumber>99999014</CustomerOrdNumber>
<LineItems>
<LineItem>
<InvtId>TEST 1235 TEST 1234</InvtId>
</LineItem>
</LineItems>
</Transaction>
</Transactions>
</XML>
If I execute the inner select (replacing "data()" with InvtId), I get what I am trying to achieve:
<InvtId>TEST 1235</InvtId>
<InvtId>TEST 1234</InvtId>
Try it like this:
Nested selects need the ,TYPE extension to come back as XML...
You might need to specify a path in the inner PATH('') or give a name to the column with AS ...
Select LTRIM(RTRIM(H.CustPONbr)) As "Transactions/Transaction/CustomerOrdNumber"
,(
Select LTRIM(RTRIM(InvtID))
From X888_Amazon_Order_Line L1
Where L1.CpnyID = H.CpnyID And L1.CustPONbr = H.CustPONbr
FOR XML PATH (''), TYPE
) As "Transactions/Transaction/LineItems/LineItem/InvtId"
From X888_Amazon_Order_Header H (nolock)
where h.CustPONbr = '99999014'
For XML PATH ('ProcessEngineSubmission'), Root ('XML')

Naming xml node based on value from DB column

Currently I have an xml structure that is built like this.
<Project>
<ProjectAttribute>
<AttributeId></AttributeId>
<FieldName></FieldName>
<Title></Title>
<DisplayRow></DisplayRow>
<DisplayColumn></DisplayColumn>
<IsRequired></IsRequired>
<RequiredErrorMessage></RequiredErrorMessage>
<DevExpressControl>
<DevExpressControlId></DevExpressControlId>
<Class></Class>
<ClientNamePrefix></ClientNamePrefix>
</DevExpressControl>
<DataType>
<DataTypeId></DataTypeId>
<Name></Name>
</DataType>
</ProjectAttribute>
</Project>
I want to rename ProjectAttribute with the value of FieldName. I am unsure how to do this at the moment, seeing how when I try to put the value of that field into the XML Path, I get an error.
Here is what my sql looks like.
Set #output = (
SELECT poa.AttributeId, pa.FieldName, pa.Title, poa.DisplayRow,
poa.DisplayColumn, poa.IsRequired, pa.RequiredErrorMessage,
(SELECT de.DevExpressControlId, de.Class, de.ClientNamePrefix
FROM SCC_DevExpressControl de
WHERE de.DevExpressControlId = pa.DevExpressControlId
FOR XML Path(''), type) AS DevExpressControl,
(SELECT dt.DataTypeId, dt.Name
FROM SCC_DataType dt
WHERE dt.DataTypeId = pa.DataTypeId
FOR XML Path(''), type) AS DataType
FROM SCC_ProjectOfficeAttribute poa
LEFT JOIN SCC_ProjectAttribute pa ON poa.AttributeId = pa.AttributeId
WHERE poa.OfficeId = #OfficeId
AND poa.Status = 1
ORDER BY poa.DisplayRow, poa.DisplayColumn
FOR XML Path('ProjectAttribute'), type, Root('Project'))
select #output
#OfficeId is just an int, and #output is simply the xml. Where I am writing in the XML Path for ProjectAttribute is what I want to replace. My question is how do I get that value in there?
UPDATE: I am still somewhat at a loss. I have tried moving things like AttributeId and FieldName into the main node itself for ProjectAttribute, but this does not really fulfill my requirements.

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