Select element from XML - sql-server

Given the following:
declare #samplexml as xml
set #samplexml = '<root><someelement><another /><somethingElse>test</somethingElse></someelement></root>'
select
#samplexml.value('/root[1]','nvarchar(max)')
I get the result:
test
But I want the result:
<root><someelement><another /><somethingElse>test</somethingElse></someelement></root>
How can I select the actual XML element? I also tried:
select
#samplexml.value('/root[1]','XML')
But I got the error The data type 'XML' used in the VALUE method is invalid..

Just use the .query() method instead of .value() :
SELECT #samplexml.query('/root[1]')
or
SELECT #samplexml.query('.')
This returns the element (and its contents) that matches that XPath expression given, and it's returned as XML type

Related

Supply xml element value in modify() method

I know how to replace element value for the xml element in the modify() method. Here's the example
TSQL Replace value in XML String
My problem is a bit different. Taking example from above link...
UPDATE dbo.TFS_Feedback_New
SET Details.modify('
replace value of (/optional/educational/text())[1]
with sql:variable("#updatedEducation")')
WHERE feedbackID = #FBID
What I want to do is provide value for 'educational'. In other words I want to do something like this
UPDATE dbo.TFS_Feedback_New
SET Details.modify('
replace value of (/optional/sql:variable("#name")/text())[1]
with sql:variable("#value")')
WHERE feedbackID = #FBID
I'm getting the following error because of sql:variable("#name")
The XQuery syntax '/function()' is not supported.
How can I pass both the name of the element to be updated and its value to my
stored procedure and have it update the XML column?
You are not allowed to use variables as part of the XPath, but you can use a predicate:
DECLARE #xml XML=
N'<root>
<optional>
<educational>SomeText</educational>
<someOther>blah</someOther>
</optional>
</root>';
--The straight approach as you know it:
SET #xml.modify('replace value of (/root/optional/educational/text())[1] with "yeah!"');
SELECT #xml;
--Now we use a variable to find the first node below <optional>, which name is as given:
DECLARE #ElementName VARCHAR(100)='educational';
SET #xml.modify('replace value of (/root/optional/*[local-name()=sql:variable("#ElementName")]/text())[1] with "yeah again!"');
SELECT #xml;
Try it out...

How parser properties in node XML file in XQUERY 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.

How to force SQL Server to return empty JSON array

I'm using SQL Server 2016, which supports JSON PATH to return JSON string.
I wonder how to get just a simple empty json array, I mean [] when my query or sub-query returns null. I've tried this query:
SELECT '' AS TEST
FOR JSON PATH,ROOT('arr')
which returns:
{"arr":[{"test":""}]}
and also this one:
SELECT NULL AS TEST
FOR JSON PATH,ROOT('arr')
which returns:
{"arr":[{}]}
it's better but still not correct, I need this:
{"arr":[]}
You can always check this with ISNULL, e.g.:
select ISNULL( (SELECT * FROM sys.tables where 1=2 FOR JSON PATH), '[]')
If you need this in app layer, maybe it would be better to check is there some results set in data access code, and if not just return [] or {}.
This works, and can be composed within another for json query:
select json_query('[]') arr
for json path, without_array_wrapper
When nesting such subqueries, I've found that combining what others have said works best, i.e.:
Using COALESCE((SELECT .. FOR JSON), '[]') to prevent the null value from the subquery
Using JSON_QUERY() to prevent the escaping / quoting.
For example:
select
json_query(coalesce((select 1 as b where 1 = 0 for json path), '[]')) as a
for json path;
Produces:
|JSON |
|----------|
|[{"a":[]}]|
Without JSON_QUERY
Now the nested json array gets quoted:
select
coalesce((select 1 as b where 1 = 0 for json path), '[]') as a
for json path;
Results in
|JSON |
|------------|
|[{"a":"[]"}]|
Without COALESCE
Now the nested JSON is null:
select
json_query((select 1 as b where 1 = 0 for json path)) as a
for json path`;
Results in
|JSON|
|----|
|[{}]|
A little manual, but if you need a quick hack, here you go:
DECLARE #JSON NVARCHAR(MAX) = (SELECT NULL AS test
FOR JSON PATH,ROOT('arr'))
SELECT REPLACE(#json, '{}', '')
By itself, JSON_QUERY('[]') AS [features] did not work for me. I found that the results were formatted as follows:
"features":"[]"
which was not desirable.
To get the desired result, I needed to store the JSON in a variable, then perform a REPLACE on the result, as follows:
DECLARE #json VARCHAR(MAX) = (SELECT JSON_QUERY('[]') AS [features],
-- Other selected fields elided for brevity
FROM MyTable
FOR JSON, WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES);
SET #json = REPLACE(#json, '"features":"[]"', '"features":[]');
SELECT #json;
Yes, it's a terrible hack. But it works, and returns the result I want. Our client absolutely must have an empty array returned, and this was the best way I could find to ensure that it was present.
Right now I had exactly the same problem, I think this is the right way to handle it according to the microsoft documentation:
DECLARE #Array TABLE(TEST VARCHAR(100));
SELECT
arr = ISNULL((SELECT TEST FROM #Array FOR JSON PATH), '[]')
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
Result:
{"arr":[]}
Using JSON_OBJECT and JSON_ARRAY functions(SQL Server 2022):
SELECT JSON_OBJECT('arr':JSON_ARRAY())

SQL Server: Text() in Xquery used to add delimiter in XML

I have following existing code in one of the stored procedures to all delimiter / between error messages encounters in the validations:
;with delimiting_errors
(Id,
Delimited_Error_List)
as
(
select
e2.Id,
'/'
+ (select ' ' + Fn
from Customer e
where e.Id = e2.Id
for xml path(''), type
).value('substring(text()[1],2)', 'varchar(max)') as Delimited_Error
from Customer e2
group by e2.Id
)
SELECT * FROM delimiting_errors
Request you to please help me in understanding the command
value('substring(text()[1], 2)', 'varchar(max)')
I tried to search about text(), but couldn't find exact documentation for the same.
Similarly, how substring function is working only on 2 parameters in substring(text()[1], 2), which actually requires 3 parameter.
Please help me with the concept behind this command, also please help me with some resource to read about Text().
What is going on here:
.value('substring(text()[1],2)', 'varchar(max)')
value() function to extract a specific value from the XML, and convert it to a SQL Server data type, in your case to varchar(max)
substring is XQuery substring, not SQL substring, here it returns substring starting at position 2
text() function here retrieves the inner text from within the XML
[1] suffix acts as an indexer, and fetches the first result matched
For more info read XQuery Language Reference, it's like "another language" inside SQL.
.value('substring(text()[1],2)', 'varchar(max)') as Delimited_Error
You use this XML-trick to concatenate values. In the beginning you add a double space select ' ' + Fn, this must be taken away for the beginning of the return string.
So, the .value returns the "substring" (XPath-Function!) of the inner text() starting at the index 2.
Find more information here: http://wiki.selfhtml.org/wiki/XML/XSL/XPath/Funktionen

SQL Server XML Type Select Where Attribute = X From Any Tag

select *
from tablename
where CONVERT(xml, Sections).value('(/sections/section/#value)[1]', 'varchar(1)') = 'f'
will properly retrieve a record with the following value in the Sections column:
<sections><section value="f" priority="4" /><section value="a" priority="4" /></sections>
But misses this:
<sections><section value="w" priority="4" /><section value="f" priority="4" /></sections>
Obviously this is the problem "/sections/section/#value)[1]" but I don't understand the syntax and Google hasn't been too helpful. I found some code that got me this far, but I don't know how to modify it so that it will look through all tags instead of just the first one. I tried dropping the [1] but that gave the following error:
XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'
You can use exist().
select *
from tablename
where CONVERT(xml, Sections).exist('/sections/section[#value = "f"]') = 1
If you want to use some dynamic value instead a hard coded f in the query you can use sql:variable().
declare #Value varchar(10) = 'f'
select *
from tablename
where CONVERT(xml, Sections).exist('/sections/section[#value = sql:variable("#Value")]') = 1
If you have multiple entries of an XML tag, you need to use the .nodes() XQuery method:
select
*,
Sections(Section).value('(#value)[1]', 'varchar(1)')
from tablename
cross apply CONVERT(xml, Sections).nodes('/sections/section') AS Sections(Section)
With this, you create a "pseudo-table" called Sections(Section) that contains one XML row for each element that matches your XPath (for each <section> under <sections>). You can then reach into this pseudo-table and extract individual bits of information from those XML "rows" using hte usual .value() method

Resources