How parser properties in node XML file in XQUERY SQL server - sql-server

I have this XML:
i want to get the value on Property name = "ParticipTypeName" i am using something like that:
;WITH XMLNAMESPACES(DEFAULT 'http://xml.common.asset.aoma.sonymusic.com/ProductMetadata.xsd')
SELECT
x.u.value('(/BusinessUnitProperties/Property[#name = "ParticipTypeName"])[1]', 'varchar(100)') as ParticipTypeName
from
#XML.nodes('/ProductMetadata/Tracks/Track/Participants/Participant') x(u)
it doesn't work.
How I should get the value in this property?

Try this:
SELECT x.u.value('(//*:Property[#*:name="ParticipTypeName"])[1]','nvarchar(max)')
The // will search for any element <Property>. The XQuery-filter will choose the one with the name you are looking for. The *: will allow you to ignore the namespace.

Related

Change value of a xml root node attribute

I am trying to modify the attribute of an XML root attribute in XQuery with T-SQL but I don't manage to do that. My XML has a namespace in it and I can't exactly bypass this. When I query the value of the xml I successfully retrieve the value because I use: ;WITH XMLNAMESPACES (DEFAULT 'some:namespace:here:v1'). I also tried to use: 'declare default element namespace "some:namespace:here:v1";' in the XQuery but does not seem to work.
Any ideas of how can I achieve this ?
This is an example of the XML I am trying to modify.
DECLARE #XML_TO_READ XML = N'
<F2101 xmlns="some:namespace:here:v1" propertyToModify="valueToModify">
<person xmlns="some:namespace:here:v1" anotherPropertyToModify="anotherValueToModify" />
</F2101>'
I retrieve the value like this:
;WITH XMLNAMESPACES (DEFAULT 'some:namespace:here:v1')
SELECT propertyToModify =
#XML_TO_READ.value('(/F2101/#propertyToModify)[1]', 'nvarchar(50)')
And I tried to modify (update) the value like this:
SET #XML_TO_READ.modify('
declare default element namespace "some:namespace:here:v1";
replace value of (/F2101/propertyToModify/text())[1] with ("modifiedValue")')
I tried multiple solutions but I did not find anything that would work for my special case here.
Thanks in advance.
Your statement should be:
SET #XML_TO_READ.modify('
declare default element namespace "some:namespace:here:v1";
replace value of (/F2101/#propertyToModify)[1] with ("modifiedValue")')
Note the "#propertyToModify" rather than "propertyToModify/text()".
Also, documentation link: replace value of (XML DML).

Select XML multiple only a few nodes with the same name

I'm trying to construct a soap message, and I was able to construct the entire message using a single select. Except the problem is, on only a few occasions the same node name is repeated twice.
So for example the required output result should be like so, with two separate id root nodes:
<SoapDocument>
<recordTarget>
<patientRole>
<id root="1.2.3.4" extension="1234567" />
<id root="1.2.3.5.6" extension="0123456789" />
</patientRole>
</recordTarget>
</SoapDocument>
I tried to use my sparse knowledge of xpath to construct the node names like so:
select
'1.2.3.4' AS 'recordTarget/patientRole/id[1]/#root',
'1234567' AS 'recordTarget/patientRole/id[1]/#extension',
'1.2.3.5.6' AS 'recordTarget/patientRole/id[2]/#root',
'0123456789' AS 'recordTarget/patientRole/id[2]/#extension'
FOR XML PATH('SoapDocument'),TYPE
Apparently xpath naming can't be applied to column names id[1] and id[2] like that? Am I missing something here or should the notation be different? What would be the easiest way to constuct the desired result?
From your question I assume, this is not tabular data, but fixed values and you are creating a medical document, assumably a CDA.
Try this:
SELECT
(
SELECT
'1.2.3.4' AS 'id/#root',
'1234567' AS 'id/#extension',
'',
'1.2.3.5.6' AS 'id/#root',
'0123456789' AS 'id/#extension'
FOR XML PATH('patientRole'),TYPE
) AS [SoapDocument/recordTarget]
FOR XML PATH('')
The result:
<SoapDocument>
<recordTarget>
<patientRole>
<id root="1.2.3.4" extension="1234567" />
<id root="1.2.3.5.6" extension="0123456789" />
</patientRole>
</recordTarget>
</SoapDocument>
Some explanation: The empty element in the middle allows you to place two elements with the same name in one query. There are various approaches how you get this into your surrounding tags. This is just one possibility.
UPDATE
I'd like to point to BdR's own answer! Great finding and worth an up-vote!
A little more elaboration on the answer from Shnugo, as it got me trying out some things using an "empty column".
If you do not give the emtpy column a name, it will reset to the XML root node. So the following columns will start from the XML root of the selection you are in at that point. However, if you explicitly name the empty separator column, then the following columns will continue in the hierarchy as set by that column name.
So the selection below will also result in the desired result. It's subtly different, but in my case it allows me to avoid using subselections.
select
'1.2.3.4' AS 'recordTarget/patientRole/id/#root',
'1234567' AS 'recordTarget/patientRole/id/#extension',
'' AS 'recordTarget/patientRole',
'1.2.3.5.6' AS 'recordTarget/patientRole/id/#root',
'0123456789' AS 'recordTarget/patientRole/id/#extension'
FOR XML PATH('SoapDocument'),TYPE
This should do the job:
WITH CTE AS (
SELECT *
FROM (VALUES('1.2.3.4','1234567'),
('1.2.3.5.6','0123456789')) V ([root], [extension]))
SELECT (SELECT (SELECT (SELECT [root] AS [#root],
[extension] AS [#extension]
FROM CTE
FOR XML PATH('id'), TYPE)
FOR XML PATH('patientRole'), TYPE)
FOR XML PATH ('recordTarget'), TYPE)
FOR XML PATH ('SoapDocument');

How do you add attributes to an existing Root XML Node using TSQL?

I've constructed some XML in TSQL.
declare #requestXML xml
set #requestXML = (
select #dataXML
for xml raw ('rtEvent')
The general output for what I now have follows the pattern resembling this:
<rtEvent>
<ctx>
.....
</ctx>
</rtEvent>
What I'd like to do now is add some attributes and values to the rtEvent root
element node but I'm not certain how to achieve it.
I've looked at the Modify method of the XML object and have observed the insert, replace value of, and delete operations but cannot seem to figure out how to use any of them to achieve the results I'm after.
Basically, I want to be able to modify the root node to reflect something like:
<rtEvent type="customType" email="someaddress#domain.com"
origin="eCommerce" wishedChannel="0" externalId="5515">
<ctx>
...
</ctx>
</rtEvent>
Should I be using the documented XML.Modify or is there a better method? How should it be done?
Just in case you wanted to see the modify method way of doing it:
DECLARE #requestXML XML = '<rtEvent><ctx>...</ctx></rtEvent>'
SET #requestXML.modify(
'insert
(
attribute type {"customeType"},
attribute email {"someaddress#domain.com"},
attribute origin {"eCommerce"},
attribute wishedChannel {"0"},
attribute externalId {"5515"}
)
into (/rtEvent)[1]')
SELECT #requestXML
it returns this:
<rtEvent type="customeType" email="someaddress#domain.com" origin="eCommerce" wishedChannel="0" externalId="5515">
<ctx>...</ctx>
</rtEvent>
Better use FOR XML PATH, which allows you to specify the naming and aliases as you like them:
SELECT 'SomeContext' AS [ctx]
FOR XML PATH('rtEvent')
This will return this:
<rtEvent>
<ctx>SomeContext</ctx>
</rtEvent>
But with the right attributes you get this:
SELECT 'customType' AS [#type]
,'someaddress#domain.com' AS [#email]
,'eCommerce' AS [#origin]
,0 AS [#wishedChannel]
,5515 AS [#externalId]
,'SomeContext' AS [ctx]
FOR XML PATH('rtEvent')
The result
<rtEvent type="customType" email="someaddress#domain.com" origin="eCommerce" wishedChannel="0" externalId="5515">
<ctx>SomeContext</ctx>
</rtEvent>

SQL Server 'To XML' Tag Name

I have the following code in the select block of my query which picks out rows from a table and outputs them in XML:
select ...
...
,substring(
(
Select RC_1.Master_Code AS [TopographyTDR]
From apex.Histo_Result_Coding as RC_1
Where RC_1.Histo_Report = Histo_Result_Coding.Histo_Report
ORDER BY RC_1.Histo_report
For XML auto
), 1, 1000) [TDRCodes]
...
and this gives an output similar to that shown below:
<RC_1 TopographyTDR="T77100"/><RC_1 TopographyTDR="T77100"/>
<RC_1 TopographyTDR="T01000"/><RC_1 TopographyTDR="T01000"/>
<RC_1 TopographyTDR="EGFR "/> <RC_1 TopographyTDR="GHER2"/>
<RC_1 TopographyTDR="T04020"/><RC_1 TopographyTDR="T04020"/>
<RC_1 TopographyTDR="T77100"/><RC_1 TopographyTDR="T77100"/>
This is the correct data, but I need the tag to be 'TopographyTDR' without the RC_1. i.e. the data should look like:
<TopographyTDR="T77100"/><TopographyTDR="T77100"/>
<TopographyTDR="T01000"/><TopographyTDR="T01000"/>
<TopographyTDR="EGFR "/> <TopographyTDR="GHER2"/>
<TopographyTDR="T04020"/><TopographyTDR="T04020"/>
<TopographyTDR="T77100"/><TopographyTDR="T77100"/>
Is there a simple way to do this? i.e. to avoid having the table name appear in the XML tag text?
Thanks in advance.
You can use for xml path instead of for xml auto and specify tag names explicitly.
Something like:
Select RC_1.Master_Code AS 'TopographyTDR'
From apex.Histo_Result_Coding as RC_1
Where RC_1.Histo_Report = Histo_Result_Coding.Histo_Report
ORDER BY RC_1.Histo_report
for XML path('')
Update:
Looking at your desired output more precisely - it doesn't looks like valid xml.
Despite on missing root node (it could be omitted for simplicity, I suppose), this format has fundamental problem: tag like <TopographyTDR="T77100"/> in fact doesn't has tag name but only has attribute TopographyTDR having value T77100. Are you sure you want such a pseudo-xml data?
Your desired format is not allowed. An XML node must have a tag name and a content or attributes. And you'll need a root...
You must use PATH instead of AUTO. Look at this:
select top 3 name
from sys.objects
for xml path(''),ROOT('root');
select top 3 name AS [#attrib]
from sys.objects
for xml path('item'),ROOT('root')

Return a calculated element name from a XML FLWOR query

I have the following XML data in SQLServer 2008 R2
DECLARE #data XML
SET #data = '<root attr1="val1" attr2="val2" attr3="val3"/>'
I would like to get a list of the attribute names from root and output that as a list of elements, like this:
<root>
<attr1>val1</attr1>
<attr2>val2</attr2>
<attr3>val3</attr3>
</root>
I have been playing around with FLWOR queries to get what I want. So far I have this:
SELECT #data.query('
for $attr in /*/#*
return <test>{fn:string($attr)}</test>
')
Which is fine and is almost what I need, but when I try and do this...
SELECT #data.query('
for $attr in /*/#*
return <{fn:local-name($attr)}>{fn:string($attr)}</{fn:local-name($attr)}>
')
its not happy. It's doesn't seem to like anything other than a hard coded element name.
How can I return an element with a computed name?
According to Microsoft,
Right now, we only support constant expressions for the name expression of computed element and attribute constructors. The work around is building dynamic sql to cancat in the attribute name.
Try computed element constructors:
SELECT #data.query('
for $attr in /*/#*
return element {fn:local-name($attr)} {fn:string($attr)}
')

Resources