Getting result of FOR XML into a variable - sql

SELECT #Name = Name FROM Table FOR XML AUTO
Does not work, how do you get the XML result from using FOR XML into a variable?

This will work:
SELECT #Name = CONVERT(XML, (
SELECT Name
FROM SomeTable
FOR XML AUTO
));
You can try it without the wrapping CONVERT(XML, (...)) statement but I've found that SQL Server doesn't like assigning to XML variables without that explicit cast.

Related

Add attribute with colon to xml node with FOR XML PATH

I am trying to modify a stored proc to contain the following:
SET #XML = (
SELECT Category.Title,
(
SELECT 'true' AS [#json:Array], Book.Name, Book.Value
FROM #Book Book
WHERE Category.CategoryID = Book.CategoryID
FOR XML PATH('Values'), ROOT('Book'), TYPE
)
FROM #Category Category
FOR XML PATH('Category'), ROOT('Response')
)
The "SELECT 'true' AS [#json:Array]" is there to force the xml to add "json:Array='true' to the values node so that even if there's only one child element it will be contained in an array. But, the #json:Array throws an error: "XML name space prefix 'json' declaration is missing for FOR XML column name '#json:Array'."
I've looked at links like this but they all seem to deal with adding attributes that don't include a colon. I've also tried adding "WITH NAMESPACES..." but couldn't get the syntax right.
Can someone tell me how to modify the SQL to have this work?
If you do it like this:
DECLARE #XML XML;
WITH XMLNAMESPACES('xmlns:json' AS json)
SELECT #XML=
(
SELECT 'YourTitle',
(
SELECT 'true' AS [#json:Array], 'BookName', 'BookValue'
FOR XML PATH('Values'), ROOT('Book'), TYPE
)
FOR XML PATH('Category'), ROOT('Response')
)
SELECT #xml
... you'll get the attribut. But the price is a repeated namespace in all of your root nodes (in nested too).
This might be a trick, but you'll have to declare your namespace in the top element:
DECLARE #XML XML;
SELECT #XML=
(
REPLACE(REPLACE(
(
SELECT 'YourTitle',
(
SELECT 'true' AS [#jsonArray], 'BookName', 'BookValue'
FOR XML PATH('Values'), ROOT('Book'), TYPE
)
FOR XML PATH('Category'), ROOT('Response')
),'jsonArray','json:Array'),'<Response','<Response xmlns:json="urnJson"')
);
SELECT #xml

Pass SQL parameter value to select xml data

Code:
DECLARE #dataxml XML = CONVERT(xml ,'<Parentnode><childnode><id>1</id></childnode></Parentnode>')
DECLARE #childnode VARCHAR(50)
SET #childnode = (SELECT DISTINCT
r.value('fn:local-name(.)', 'nvarchar(50)') as t
FROM #dataxml.nodes('//Parentnode/*') AS records(r))
SELECT #childnode
SELECT
t.value('id[1]', 'int') AS id
FROM
#dataxml.nodes('/Parentnode/*[local-name(.)=sql:variable("#childnode")]/*') AS XD(t)
Output is returned as NULL, but it should comes with id value in xml
What's wrong in this code?
You just need to remove the /* characters at the end of your last line, like this:
#dataxml.nodes('/Parentnode/*[local-name(.)=sql:variable("#childnode")]') AS XD(t)
Also I had to remove the $ character at the end of the FROM keyword, but I'm guessing that could just be a typo. I tested this is SQL Server 2012.

Querying XML data types which have xmlns node attributes

I have the following SQL query:
DECLARE #XMLDOC XML
SET #XMLDOC = '<Feed><Product><Name>Foo</Name></Product></Feed>'
SELECT x.u.value('Name[1]', 'varchar(100)') as Name
from #XMLDOC.nodes('/Feed/Product') x(u)
This returns:
Name
----
Foo
However, if my <Feed> node has an xmlns attribute, then this doesn't return any results:
DECLARE #XMLDOC XML
SET #XMLDOC = '<Feed xmlns="bar"><Product><Name>Foo</Name></Product></Feed>'
SELECT x.u.value('Name[1]', 'varchar(100)') as Name
from #XMLDOC.nodes('/Feed/Product') x(u)
Returns:
Name
----
This only happens if I have an xmlns attribute, anything else works fine.
Why is this, and how can I modify my SQL query to return results regardless of the attributes?
If your XML document has XML namespaces, then you need to consider those in your queries!
So if your XML looks like your sample, then you need:
-- define the default XML namespace to use
;WITH XMLNAMESPACES(DEFAULT 'bar')
SELECT
x.u.value('Name[1]', 'varchar(100)') as Name
from
#XMLDOC.nodes('/Feed/Product') x(u)
Or if you prefer to have explicit control over which XML namespace to use (e.g. if you have multiple), use XML namespace prefixes:
-- define the XML namespace
;WITH XMLNAMESPACES('bar' as b)
SELECT
x.u.value('b:Name[1]', 'varchar(100)') as Name
from
#XMLDOC.nodes('/b:Feed/b:Product') x(u)
As well as the XMLNAMESPACES solution, you can also use the hideously bulky local-name syntax...
DECLARE #XMLDOC XML
SET #XMLDOC = '<Feed xmlns="bar"><Product><Name>Foo</Name></Product></Feed>'
SELECT x.u.value('*[local-name() = "Name"][1]', 'varchar(100)') as Name
from #XMLDOC.nodes('/*[local-name() = "Feed"]/*[local-name() = "Product"]') x(u)
You can define namespaces like:
WITH XMLNAMESPACES ('bar' as b)
SELECT x.u.value('b:Name[1]', 'varchar(100)') as Name
FROM #XMLDOC.nodes('/b:Feed/b:Product') x(u)

Stored procedure parameter with XML query in MSSQL gives "argument must be string literal"

I'm trying to query a table with a column which is xml data with the query and value functions. When using regular string literals it's all okay, but if I put that in a stored procedure and try to use variables it doesn't work.
I suppose I'm not using the correct datatype, but after some searching I can't figure out what datatype the query function wants.
Example:
table contains
| Id | xmldata |
| 1 | <data><node>value</node></data> |
now, using the select query
select id
from table
where xmldata.query('/data/node').value('.', 'VARCHAR(50)') = 'value'
gets me the data I want. But, if I use this in a stored procedure and use a parameter #xpath varchar(100) and pass that to the query method as xmldata.query(#xpath)
i get the error
The argument 1 of the xml data type method "query" must be a string literal.
I guess varchar(100) is not correct, but what datatype can I use that would make MSSQL happy?
Update:
Okay, so. Apparently you can't pass a parameter to the query method "just like that", but one can use the sql:variable in conjunction with local-name to work a part of it out. So, for instance, this will work
declare #xpath VarChar(100)
set #xpath='node'
select objectData.query('/data/*[local-name() = sql:variable("#xpath")]')
.value('.', 'varchar(100)') as xmldata
from table
and value is selected in the column xmldata. But(!) it requires that the root node is the first value in the query function. The following will not work
declare #xpath VarChar(100)
set #xpath='/data/node'
select objectData.query('*[local-name() = sql:variable("#xpath")]')
.value('.', 'varchar(100)') as xmldata
from table
notice how the query path is "moved up" to the variable. I will continue my investigations..
A literal is the opposite of a variable. The message means that you cannot pass a variable as the first argument to query.
One way around that is dynamic SQL:
declare #sql varchar(max)
set #sql = 'select id from table where xmldata.query(''' + #path +
''').value(''.'', ''VARCHAR(50)'') = ''value'''
exec #sql
As you can see, dynamic SQL does not result in very readable code. I would certainly investigate alternatives.
EDIT: Your suggestion of local-name() works for node names.
declare #nodename varchar(max)
set #nodename = 'node'
...
where xmldata.query('//*[local-name()=sql:variable("#nodename")]')
.value('.', 'varchar(50)') = 'value'
There doesn't seem to be an equivalent for paths.

T-SQL XML get a value from a node problem?

I have an XML like:
<?xml version="1.0" encoding="utf-16"?>
<ExportProjectDetailsMessage xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Project">
<CPProjectId>7665699f-6772-424c-8b7b-405b9220a8e7</CPProjectId>
</ExportProjectDetailsMessage>
I'm trying to get the CPProjectId as a Uniqueidentifier using:
DECLARE #myDoc xml
DECLARE #ProdID varchar(max)
SET #myDoc = '<ExportProjectDetailsMessage xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Project"><CPProjectId>7665699f-6772-424c-8b7b-405b9220a8e7</CPProjectId></ExportProjectDetailsMessage>'
SET #ProdID = #myDoc.value('(ExportProjectDetailsMessage/CPProjectId)[1]', 'varchar(max)' )
SELECT #ProdID
All i can receive is NULL =/
I've tried many combinations on #myDoc.value but no results =/
How can i retrieve the value from my XML ?
Thanks!
--EDIT:
Something that i noted, when i remove the namespace declaration from the XML it works fine!
The problem is that i need this namespaces! =/
You're right the namespace is the issue. You're query is looking for a node ExportProjectDetailsMessage but such a node doesn't exist in your document, because there is a namespace declared as a default in your document. Since you can't remove that (nor should you) you should include it in your XPATH query like so:
set #ProdId = #myDoc.value('
declare namespace PD="http://schemas.datacontract.org/2004/07/Project";
(PD:ExportProjectDetailsMessage/PD:CPProjectId)[1]', 'varchar(max)' )
You may also want to consider not using varchar(max) but perhaps uniqueidentifier
A better way to do this is to simply declare the namespace before each of your queries:
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.datacontract.org/2004/07/Project')
It's like a temporary default. When you run the next query in the batch you'll get nulls again if you don't specify this before each of your selects.
So instead of using "SET", you can use "SELECT" to set the value like so:
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.datacontract.org/2004/07/Project')
SELECT #ProdID = #myDoc.value('(ExportProjectDetailsMessage/CPProjectId)[1]', 'VarChar(MAX)')
SELECT #ProdID
Same results, just more readable and maintainable.
I found the solution here: http://www.sqlservercentral.com/Forums/Topic967100-145-1.aspx#bm967325