SQL Result to XML (Specific Layout) - sql-server

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/

Related

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...

Generate XML in encoding UTF-8 using SQL Server

I want to generate an XML in UTF-8 encoding. But by default it is generating in UCS-2.
Please help me to generate XML in UTF-8 encoding. Below is my query:
select
isnull(cast('5678' as nvarchar(50)),'') [Vlootnummer],
isnull((select top 1 cast(EngineNo as nvarchar(50))
from VTS_DEMO.dbo.VehicleDetails v
join VTS_DEMO.dbo.VehicleDevice vd on v.VehicleId = vd.VehicleId
and ObuID = '353234023894171'), '') as Kenteken,
isnull((select top 1 cast(FillingStationName as nvarchar(50)) Units
from VTS_DEMO.dbo.FillingStation
where GeoFenceId = 3655),'') Locatie,
isnull((select top 1 GeofenceCode
from VTS_DEMO.dbo.GeoFence
where GeoFenceId = 3655), '') GeoFencingID,
isnull(cast(case when 1 = 0 then '' else '2017-02-07T23:15:25Z' end as nvarchar(50)),'') Aankomsttijd,
isnull(cast(case when 1 = 0 then '2017-02-07T23:15:25Z' else NULL end as nvarchar(50)),'') Vertrektijd
FOR XML PATH('Notificatie')
When I send this XML as attachment in mail using stored procedure msdb.dbo.sp_send_dbmail, when its opened in notepad++ then it shows UCS-2.
As per the MSDN documentation, It is a limitation on the XML datatype on MS SQL Server. SQL Server always saves an XML datatype with the character encoding of UCS-2.
The XML declaration PI, for example, , is not preserved when storing XML data in an xml data type instance. This is by design. The XML declaration () and its attributes (version/encoding/stand-alone) are lost after data is converted to type xml. The XML declaration is treated as a directive to the XML parser. The XML data is stored internally as ucs-2. All other PIs in the XML instance are preserved.
Considering the above, you can add this manually to take affect.
In an attempt to do this, you can use the following to gain advantage of FOR XML
SELECT CAST((SELECT [Columns] FROM [Tables] FOR XML AUTO) AS VARCHAR(MAX)) AS xmldata
You can check this answer for a discussion similar to this.
Hope this helps!

Join query with user provided data-T-sql

I have a requriment where user will provide many Ids(in Hundres/thousands) in a Text area in vb.net app, I need to use these IDs in T-Sql(Sql Server) to get the data. I dont want to save these Ids in any database table. Just want to pass using a paramater (type of varchar(max)) and use in the procedure.
actually, only read access is permitted for the vb application users.It is Sql-2005 database.Id field is atleaset 12 to 15 characters length.The user will copy/paste data from other source may be CSV or Excel file.
any idea how can i achive this.
any help is appreciated.
Thanks
If you do not want to use Table Valued Parameters, as suggested elsewhere, and you don't want to store the ID's in a temporary table, you can do the following.
Assuming your ID's are integer values, that are separated by commas, in the parameter string, you can use the LIKE operator in your SQL-statement's WHERE filter:
Say you have a parameter, #IDs of type varchar(max). You want to get only those records from MyTable where the ID-column contains a value that has been typed into the #IDs-parameter. Then your query should look something like this:
SELECT * FROM MyTable
WHERE ',' + #IDs + ',' LIKE '%,' + CAST(ID AS varchar) + ',%'
Notice how I prepend and append an extra comma to the #IDs parameter. This is to ensure that the LIKE operator will still work as expected for the first and last ID in the string. Make sure to take the nescessary precautions against SQL injection, for example by validating that users are only allowed to input integer digits and commas.
Try using CHARINDEX:
DECLARE #selectedIDs varchar(max) = '1,2,3'
Set #selectedIDs = ',' + #selectedIds + ',' --string should always start and end with ','
SELECT * FROM YourTable
WHERE CHARINDEX(',' + CAST(IDColumn as VARCHAR(10)) + ',', #selectedIds) > 0

Querying XML data in sql server 2005

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.

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