Querying XML data in sql server 2005 - sql-server

i got a snippet which show how to query xml data in sql server but few area still not clear to me.
DECLARE #xml XML
SET #xml = '<root>
<row>one</row>
<row>two</row>
<row>three</row>
</root>'
CREATE TABLE #Fields(Field varchar(MAX))
INSERT INTO #Fields
SELECT
x.y.value('text()[1]', 'varchar(5)')
FROM #xml.nodes('root/row') x(y)
SELECT * FROM #Fields
DROP TABLE #Fields
what is x.y i just do not understand and also what kind of syntax is it 'text()[1]', 'varchar(5)'
if text() is in-built function then does it work for any data type ?
please help me to visualize what x.y ?? thanks

The .nodes() call converts your XML into a list of XML fragments - and a "list" in SQL Server always is a table - so this is really a table alias (x) and a column alias (y) for an internally constructed tables of XML fragments (one "row" per match for .nodes())
If you would be using .nodes('/root)` then you'd get an "pseudo table" with these rows:
Table x
y
----------------
<row>one</row>
<row>two</row>
<row>three</row>
Since you're using .nodes('/root/row') instead, you're really getting just the values of those <row> elements:
Table x
y
----------------
one
two
three
The .value() now takes one of those XML fragments and returns something from it. It could be the name of a sub-element - but text() (yes, a built-in XQuery function) just converts the whole XML fragment into a textual representation. The second parameter of the .value() call just defines what that value should be treated as - what datatype.

Related

SQL Server : convert Varchar to XML - then operate on it and select data?

I got a Varchar column which holds XML. I would like to convert them to JSON so I did a quick test.
Here is what worked so far:
DECLARE #xml XML
SET #XML = '<content>
<value>123456</value>
</content>'
SELECT
'{"foo":"' + b.value('(./value)[1]', 'VARCHAR(50)') + '"}'
FROM
#xml.nodes('/content') AS a(b)
Result:
{"foo":"123456"}
So far so good, but this does not do half the job that has to be done.
I have lots of rows, and I want to convert all rows at once.
I have multiple values inside a content tag and therefore I cannot just add a json prefix and suffix since inside that array there will be more than one ID.
I read this article : https://learn.microsoft.com/en-us/sql/t-sql/xml/nodes-method-xml-data-type?view=sql-server-ver15 but since my target column is a VARCHAR and has to be cast, it actually didn't help me...
So first I cast the values to xml:
SELECT CAST(mytextField.TEXTVALUE AS XML) AS xmlData
FROM myDataTable
WHERE mytextFieldId = 12345
This returns the values as XML:
<content><value>12345</value></content>
<content><value>9874</value></content>
<content><value>Foo</value><value>Bar</value></content>
Now I'm stuck.
How can I select this result in a new query?
What would be the best approach to convert this to json format?
In the first snippet I added a prefix and a suffix to the value of the xmls value tag. How could I do this with the cast rows I selected?

SQL Result to XML (Specific Layout)

I have a table in SQL that contains a list of settings for an application per serverID
I want write a SQL statement that produce a specific XML layout.
The basic SQL statement to retrieve this data:
SELECT SettingName, SettingValue
FROM dbo.ServerSettings
WHERE ServerID = #ServerID
I just need to know the correct FOR XML options.
I want to get back a result like the following. Basically using the value of the SettingName field to be the name of the node.
<ROOT>
<COSTRECOVERYSYSTEM_CONNECTION_STRING></COSTRECOVERYSYSTEM_CONNECTION_STRING>
<COSTRECOVERYSYSTEM_EXTRACT_INTERVAL>60</COSTRECOVERYSYSTEM_EXTRACT_INTERVAL>
<COSTRECOVERYSYSTEM_FILE_DATESTAMP>yyyyMMdd</COSTRECOVERYSYSTEM_FILE_DATESTAMP>
<COSTRECOVERYSYSTEM_FILE_EXTENSION>txt</COSTRECOVERYSYSTEM_FILE_EXTENSION>
<COSTRECOVERYSYSTEM_FILE_NAME>txt</COSTRECOVERYSYSTEM_FILE_NAME>
<COSTRECOVERYSYSTEM_FILE_PATH>txt</COSTRECOVERYSYSTEM_FILE_PATH>
</ROOT>
As in any other query you cannot use a column's value as the output column name. This would need some dynamically created statement and EXEC() for its execution.
But you might do something along this:
DECLARE #tbl TABLE(SettingName VARCHAR(100),SettingValue VARCHAR(100));
INSERT INTO #tbl VALUES ('Setting1','1'),('Setting2','2'),('ForbiddenValue','Huh! What about & and <?');
SELECT CAST((
SELECT '<' + UPPER(t.SettingName) + '>' +
--this embedded FOR XML will implicitly do the escaping for you
(SELECT t.SettingValue AS [*] FOR XML PATH('')) +
'</' + UPPER(t.SettingName) + '>'
FROM #tbl t
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)') AS XML)
FOR XML PATH('ROOT');
In general I would never create XML with string methods. There are so many possible draw backs and traps. But in this case it might be the best choice.
Hint: Be sure, that the setting names are valid XML element names. There are some XML element naming rules
You can see these links for more information:
1. https://learn.microsoft.com/en-us/sql/relational-databases/xml/for-xml-sql-server?view=sql-server-2017
2. https://www.red-gate.com/simple-talk/sql/learn-sql-server/using-the-for-xml-clause-to-return-query-results-as-xml/

How to read XML items or data in a SQL Server stored procedure?

It looks like we are trying to parse XML and certain values? Do we do that always with T.item.value? and T(Item) an alias here? I found reference here https://learn.microsoft.com/en-us/sql/relational-databases/xml/load-xml-data?view=sql-server-2017 but still need clarification.
Need to understand what this code is doing and where can I learn more about writing such code like what is T, T(item), T.item.value here..
CREATE PROCEDURE [dbo].[MyTestSP]
#exServers xml = N'<a />' --sample: N'<a><s>abc.com</s><s u="user#example.com>outlook.com</s></a>'
AS
BEGIN
select
T.item.value('.', 'nvarchar(256)') as ExServer,
T.item.value('#u', 'nvarchar(256)') as Account
from #exServers.nodes('a/s') T(item)
END
GO
You can start by going through the tutorial Stairway to XML. After that all will be clear to you.
what is T?
T is an alias for a table of shredded xml fragments from #exServers.
what is T(item)?
This specifies the name T of the derived table from shredding the nodes and it is giving the column with the XML fragments a name item.
T.item.value
It is a way to extract a value from the xml in column item from the alias T.
nodes() Method (xml Data Type)
value() Method (xml Data Type)

Empty tags formatting in XML column in SQL Server

I have xml like this:
<root>
<name></name>
</root>
When I save it do database to XML column and query e.g in SSMS, it is formatted with self closing tags:
<root>
<name />
</root>
Is it possible to keep original formatting, or determine the formatting in SELECT statement?
If you are getting the data in a non-xml format, and don't want a Self closing tag, you'll need to replace the NULL with an empty string: ISNULL([YourColumn],'').
For example:
CREATE TABLE #Sample ([name] char(1));
INSERT INTO #Sample
VALUES(NULL);
SELECT ISNULL([Name],'') AS [name]
FROM #Sample
FOR XML PATH('root');
DROP TABLE #Sample;
If, however, you're inserting that xml into SQL Server, as an xml type, and then returning it, then SQL Server will use self-closing tags (as per my comment on the question).
As #DavidG said, any good xml parser will be able to read both self closing and non-self closing tags. If your parser can't read self closing tags, you need to consider updating your parser. If it's purely for display purposes... Well why are you using the "old" way of doing it for display?
The self-closing element <SomeElement/> is - semantically - the same as <SomeElement></SomeElement>. You should not bother about this... If your reading tool (or a third party requirement) needs this, you should rather replace this tool or discuss this with your partner.
The problem is: You have no control, that things stay as they are. Even in cases, where you are able to store the empty value with an opening and a closing tag this might be changed implicitly with a later call.
Try this:
DECLARE #tbl TABLE(ID INT IDENTITY,YourXML XML);
INSERT INTO #tbl VALUES
(
(SELECT '' AS [SomeTag] FOR XML PATH('RowNode'),ROOT('RootNode'),TYPE)
)
,(
(SELECT '' AS [SomeTag] FOR XML RAW('RowNode'),ROOT('RootNode'), ELEMENTS)
)
,(
N'<RootNode>
<RowNode>
<SomeTag></SomeTag>
</RowNode>
</RootNode>'
)
,
(CAST(
N'<RootNode>
<RowNode>
<SomeTag></SomeTag>
</RowNode>
</RootNode>'AS XML)
);
DECLARE #FirstXml XML=(SELECT YourXml FROM #tbl WHERE ID=1);
INSERT INTO #tbl VALUES(#FirstXml);
INSERT INTO #tbl SELECT #FirstXml;
INSERT INTO #tbl SELECT #FirstXml.query(N'.');
SELECT * FROM #tbl
The result
ID YourXML
--FOR XML PATH created - why ever - both tags
1 <RootNode><RowNode><SomeTag></SomeTag></RowNode></RootNode>
--FOR XML AUTO and all implicit casts use self-closing tags
2 <RootNode><RowNode><SomeTag /></RowNode></RootNode>
3 <RootNode><RowNode><SomeTag /></RowNode></RootNode>
4 <RootNode><RowNode><SomeTag /></RowNode></RootNode>
--Here we insert the first node *as is*
5 <RootNode><RowNode><SomeTag></SomeTag></RowNode></RootNode>
6 <RootNode><RowNode><SomeTag></SomeTag></RowNode></RootNode>
--But `.query()` will reformat this
7 <RootNode><RowNode><SomeTag /></RowNode></RootNode>
Some background:
XML is not stored as string string representation you see but as a hierarchy-table. Whenever you get the XML displayed on screen, its string representation is rebuilt from scratch. This can be slightly different each time you call it (e.g. attribute's order, CDATA sections, empty elements).
If you need this you can only enforce this format on string level. You might use some kind of RegEx approach, to replace any <abc/> with <abc></abc>.
But again: You should not have to think about that...
UPDATE
Try the code above with this SELECT:
SELECT *
,CAST(YourXML AS NVARCHAR(MAX)) AS CastedToString
FROM #tbl
The result
ID YourXML
CastedToString
--implicitly changed to self-closing tags
1 <RootNode><RowNode><SomeTag></SomeTag></RowNode></RootNode>
<RootNode><RowNode><SomeTag/></RowNode></RootNode>
--Self-closing **without a blank!!!**
2 <RootNode><RowNode><SomeTag /></RowNode></RootNode>
<RootNode><RowNode><SomeTag/></RowNode></RootNode>
You can see, that a comparisson on string level is not that easy... and rather hard to predict... You might use CAST(YourXml.query(N'.') AS NVARCHAR(MAX)) to get the same action done on each of your XMLs...

How to get the xml-safe version of an sql server XML Column

Is there a way to get the xml-safe version of an xml column in sql server ?
By xml-Safe i mean escaping special characters like <,>,', &, etc.
I'd like to avoid doing the replacements myself. Is there a build in function in sql server.
What I want to achieve is to store the xml content into another xml attribute.
It is not a direct answer to this question but to anyone who tries to xml-escape strings in TSQL, here is a little function I wrote :
CREATE FUNCTION escapeXml
(#xml nvarchar(4000))
RETURNS nvarchar(4000)
AS
BEGIN
declare #return nvarchar(4000)
select #return =
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(#xml,'&', '&')
,'<', '<')
,'>', '>')
,'"', '"')
,'''', ''')
return #return
end
GO
I assume that by xml-safe you mean escaping of XML special tags. If you have an XML column you wish to include in another XML document then you have two options:
project the column as [*]: select ..., xmlcolumn as [*], ... from ... for xml path... this will embed the XML content of the column in the result XMl. Eg. if the column has the value <element>value</element> then the result will be like <root><row><element>value</element></row></root>.
project the column as the column name: select ..., xmlcolumn, ... from ... for xml path... this will insert the content of the column as a value (ie. it will escape it). Eg. the same value as above will produce <root><row><xmlcolumn><element><value</element>.
If your question is about something else, then you're going to have to rephrase it in a proper manner and use terms correctly. Don't invent new terms no one understands but you.
Update:
If you are inserting XML values into the column, then you don't have to do anything at all. The client libraries know how to handle the proper escaping. As long as you write your code correctly. Remeber, XML is NOT a string and should never, ever be treated as one. If you write XML in your client, use an appropriate XML library (XmlWriter, XML DOM, Linq to XML etc). when passing in the XML into SQL Server, use the appropiate type: SqlXml. Stored procedures should use the appropiate parameter type: XML. When you read it, use the appropriate method to read XML: GetSqlXml(). Same goes for declaring the type in one of the miriad designers (LINQ to SQL , EF etc). Ultimately, there is never any need to escape XML characters manually. If you find yourself doing that, you're using the wrong API and you have to go back to the drawing board.
A good start reading is XML Support in Microsoft SQL Server 2005.
And finally, to manipulate XML as you describe (update XML column of table A with XML column of table B), you use XML methods, specifically modify (... insert...), and you bind the table B column inside the XQuery using sql:column:
update A
set somecolumn.modify('insert {sql:column("B.othercolumn")} before somenode')
from A join B on ...;
In you comment you threat XML as a string and, as I already said, you should never ever do that: strings and XML are as water and oil.
Another simpler way to xml escape a string is to use the following:
SELECT #String FOR XML PATH('')
e.g.
DECLARE #Input NVARCHAR(4000) = 'bacon & eggs'
DECLARE #String = (SELECT #Input FOR XML PATH(''))
then use #string from there
The contents of an XML column are XML. By definition, that is "XML-safe".
Do you need to include XML from a column in an XML element or attribute of another XML document? Then just save the outer XML as a string in the new document.

Resources