SQL - Define/Modify XML with variables - sql

I'm trying to define an XML variable with VARCHAR characters inside of it...
Example
DECLARE #number nvarchar(10);
SET #number = '10'
DECLARE #xml XML;
SET #xml =
'
<Root>
<Node> Hello {sql:variable("#number")} </Node>
</Root>
'
However, the problem is that you can only set xml variables to string literals. Is there any way of doing this? Thanks!

Not sure why you want to do this. Use + to concatenate the variable inside the string then assign it to xml varibale
DECLARE #number NVARCHAR(10);
SET #number = '10'
DECLARE #xml XML;
SET #xml = '
<Root>
<Node> Hello ' + Isnull(#number,'') + ' </Node>
</Root>
'
To view the xml
select #xml
Result :
<Root>
<Node> Hello 10 </Node>
</Root>

DECLARE #number nvarchar(10) = '10';
DECLARE #xml XML = N'';
SELECT #xml = #xml.query('
<Root>
<Node>Hello {sql:variable("#number")}</Node>
</Root>
');
SELECT #xml;
/*
<Root>
<Node>Hello 10</Node>
</Root>
*/

In my eyes the existing answers (upvoted and accepted) are no good paths to go. One should (almost) never create XML via string concatenation...
Why not the simple way? Let the XML-engine do the hard work!
DECLARE #number nvarchar(10);
SET #number = '10'
DECLARE #xml XML=(SELECT 'Hello ' + #number FOR XML PATH('Node'),ROOT('Root'),TYPE);
SELECT #xml;
Returns
<Root>
<Node>Hello 10</Node>
</Root>
Just imagine, your (string!) variable includes some text with forbidden characters (><& or qoutes or one of the many special / far east / whatever characters)... Using FOR XML ... will implicitly do all the escaping for you...
One side effect: If your variable is NULL you will not get a NULL for all, but a valid XML with an empty node:
<Root>
<Node />
</Root>

Related

Is there a way to put a header and footer in XML output when using SQL

I’m writing a query that returns data in XML format and I need to add a header that contains some static data, and a footer that contains a count of all of the records in the file. is there a way to do that so that the header and footer only appear once in the file?
my example query is as follows:
SET QUOTED_IDENTIFIER on
SET NOCOUNT ON
DECLARE #XMLTEST XML
SELECT #XMLTEST = (
SELECT GOLFER_NAME AS [GENERAL/NAME],
GOLFER_ID AS [GENERAL/ID],
HANDICAP_INDEX AS [ATTRIBUTES/HANDICAP],
GOLFER_AGE AS [ATTRIBUTES/AGE]
FROM GOLFERS
FOR XML PATH ('row'), Root('root')
)
SET #XMLTEST.modify(
'insert
(
attribute xsi:noNamespaceSchemaLocation {"ARIS_Inbound-v4.0.xsd"}
)
into (/GolfData)[1]')
SELECT #XMLTEST
This returns data in the following format:
<root>
<row>
<general>
<name>woods,tiger</name>
<id>1</id>
</general>
<attributes>
<handicap>4</handicap>
<age>45</age>
</attributes>
</row>
<row>
<general>
<name>fowler,ricky</name>
<id>2</id>
</general>
<attributes>
<handicap>7</handicap>
<age>39</age>
</attributes>
</row>
</root>
Which is what i am looking for but I need to have a header in the file with a format of:
<header>
<version>1.2.113</version>
<author>lincoln,abraham</author>
</header>
and a footer that counts the number of entries (or rows in this case) like this:
<trailer>
<rowcount>2</rowcount>
</trailer>
is this possible?
perhaps I'm overlooking the obvious, but couldn't you concatenate your normal results with your header and footer? Something like:
declare #header varchar(max) = "your header"
declare #footerstart varchar(max) = "your footer start"
declare #footercount int;
declare #footerend varchar(max) = "your footer end"
declare #xmlMiddle varchar(max)
select #xmlMiddle = -- your code that generates the full XML.
--add a select count(*) from your dataset like
select #footercount = from.... where...
select #header + #xmlMiddle + #footerstart + #footercount + #footerend

SSMS Obfuscation of string

I need to create a script to obfuscate some data.
The string is quite long and I need to obfuscate only some parts of it.
In the table the records are similar to this:
<?xml version="1.0" encoding="UTF-8"?><CONTRACT><IBC IBC_REF="f45f1231234ae5ac2easdasdfde5dfd" IBC_TYPE="I" TELEPHONE_1="1111111" TELEPHONE_2="11111111" MOBILE_PHONE="11111111" E_MAIL="asdasdasd#hotmail.com" SOLICITATION_MAIL="0" ARREARS_MAIL="1" MAIL_REDIRECTED="0" TITLE="Mrs" SURNAME_REGISTERED_NAME="Assadasd"
And it needs to become like this:
<?xml version="1.0" encoding="UTF-8"?><CONTRACT><IBC IBC_REF="f45f1231234ae5ac2easdasdfde5dfd" IBC_TYPE="I" TELEPHONE_1="Telephone-1" TELEPHONE_2="Telephone-2" MOBILE_PHONE="MobilePhone" E_MAIL="email-1" SOLICITATION_MAIL="0" ARREARS_MAIL="1" MAIL_REDIRECTED="0" TITLE="Mrs" SURNAME_REGISTERED_NAME="Surname"
How can I update all the rows of the table and change only some of the strings by saving the other words?
Seemingly you are working with XML strings, so I would suggest to use the SQL Server XML functionalities. Following a short example:
DECLARE #input NVARCHAR(4000) = '<?xml version="1.0" encoding="UTF-8"?><CONTRACT><IBC IBC_REF="f45f1231234ae5ac2easdasdfde5dfd" IBC_TYPE="I" TELEPHONE_1="1111111" TELEPHONE_2="11111111" MOBILE_PHONE="11111111" E_MAIL="asdasdasd#hotmail.com" SOLICITATION_MAIL="0" ARREARS_MAIL="1" MAIL_REDIRECTED="0" TITLE="Mrs" SURNAME_REGISTERED_NAME="Assadasd" /></CONTRACT>';
DECLARE #x xml = CONVERT(xml, REPLACE(#input,'encoding="UTF-8"','encoding="UTF-16"'));
SELECT #x.value('(/CONTRACT/IBC/#TELEPHONE_1)[1]', 'nvarchar(100)');
DECLARE #y xml = #x
SET #y.modify('replace value of (/CONTRACT/IBC/#TELEPHONE_1)[1] with "TELEPHONE_1"');
SELECT #y.value('(/CONTRACT/IBC/#TELEPHONE_1)[1]', 'nvarchar(100)');
SELECT #y
DECLARE #output NVARCHAR(4000) = '<?xml version="1.0" encoding="UTF-8"?>' + CONVERT(NVARCHAR(4000), #y)
SELECT #output

Parse the XML in SQL Server

<ROOT>
<arn>arn001</arn>
<arn>arn002</arn>
</ROOT>
Tried the following code though
SELECT
ARN.value('(//arn/text())[1]','VARCHAR(100)') AS arns --TAG
FROM
#xml.nodes('/ROOT')AS TEMPTABLE(ARN)
It returns only first value
Try this way :
declare #xml xml = '<ROOT>
<arn>arn001</arn>
<arn>arn002</arn>
</ROOT>'
SELECT
X.value('.','VARCHAR(100)') AS arns
FROM
#xml.nodes('/ROOT/arn')AS T(X)
SQL Fiddle

How to extract specific string part from the SQL String variable

I have an Sql Variable which contains a string value and want to extract specific part from that string value,
Declare #val as nvarchar(1000),
#outval as nvarchar(1000)
set #val='<Message>
<Header>
<MessageID>000000015</MessageID>
<MessageType>QualityData</MessageType>
<Sender>Data</Sender>
<Recipient>Data1</Recipient>
<PublishedDate>2013-12-22T13:15:23</PublishedDate>
</Header>
<Body>
<ID>150105</ID>
<Data>
<Count>5</Count>
<Brix>110</Brix>
<CO2>110</CO2>
</QualityData>
</Body>
</Message>'
Now i need to extract string from to using sql query. Output should be like as below.
<Body>
<ID>150105</ID>
<Data>
<Count>5</Count>
<Brix>110</Brix>
<CO2>110</CO2>
</Data>
</Body>
any suggestion to get this format using SQL Server.
This query may help you:
Declare #val as nvarchar(1000),
#outval as nvarchar(1000),
#start as int,
#end as int,
#len as int
set #val='<Message>
<Header>
<MessageID>000000015</MessageID>
<MessageType>QualityData</MessageType>
<Sender>Data</Sender>
<Recipient>Data1</Recipient>
<PublishedDate>2013-12-22T13:15:23</PublishedDate>
</Header>
<Body>
<ID>150105</ID>
<Data>
<Count>5</Count>
<Brix>110</Brix>
<CO2>110</CO2>
</QualityData>
</Body>
</Message>'
select #val= REPLACE(#val,'<Message>','');
select #val= REPLACE(#val,'</Message>','');
SELECT #start= CHARINDEX('<Body>', #val);
SELECT #end=CHARINDEX('</Body>', #val);
select #len= LEN (#val)
SELECT #outval= SUBSTRING ( #val ,#start ,#end- #start )
print #outval
Use a substring to extract the information and patindex to find the start character and length as below:
Select Substring(#val,PATINDEX('%<body>%', #val),PATINDEX('%</body>%', #val) - PATINDEX('%<body>%', #val) + 7)
Following your statements, I added the below line to your code. I have used the SUBSTRING and CHARINDEX in my case to solve this one, and I got exactly what you are looking for. Here is my Code: SELECT SUBSTRING(#VAL, CHARINDEX('<Body>', #VAL), CHARINDEX('</QualityData>', #VAL) - CHARINDEX('<Body>', #VAL) + LEN('</QualityData>')) This worked fine for me.

Unable to retrive XML elements value using XPATH when element has attributes

The task i have to grab the elements of a given XML , generate xpath for each element and then retrieve the values of each element:
I am able to create step one and two but when an element has attributes the XPATH doesn't work:
so if i had the following XPATH:
/Envelope[1]/Body[1]/sVerify[1]/verifyPost[1]/Message[1]/Error[1]
/Envelope[1]/Body[1]/sVerify[1]/verifyPost[1]/scope[1]/machine[1]/space[1]
/Envelope[1]/Body[1]/sVerify[1]/verifyPost[1]/scope[1]/Date[1]
It works for Below XML and am able to get elements value correctly:
-- Works: I can retrieve the Elements values using XPATH
<Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<sVerify>
<verifyPost>
<scope>
<machine>
<name>test</name>
<space>test2</space>
</machine>
<Sys>internal</Sys>
<Date>2013-02-28</Date>
</scope>
<Message>
<Error>11111111111</Error>
<Descrip>222222222</Descrip>
</Message>
<Final>true</Final>
<Receipt>33333</Receipt>
</verifyPost>
</sVerify>
</Body>
</Envelope>
Notice that i had to manually remove all the attirbutes in order for XPATH to work. It doesn't work if the XML was as below:
-- Doesn't work: can't get the elements value
<Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<sVerify xmlns="http://www.myCompany.com/Location/2014">
<verifyPost>
<scope>
<machine xmlns:i="http://www.myCompany.com/Location/2014">
<name>test</name>
<space>test2</space>
</machine>
<Sys>internal</Sys>
<Date>2013-02-28</Date>
</scope>
<Message xmlns="http://www.myCompany.com/Location/2014">
<Error>11111111111</Error>
<Descrip>222222222</Descrip>
</Message>
<Final xmlns="http://www.myCompany.com/Location/2014">true</Final>
<Receipt>33333</Receipt>
</verifyPost>
</sVerify>
</Body>
</Envelope>
There can be any number of attributes so i never know in advance what the attirbutes are going to be. What is the correct way to ensure an XPATH will always find the value of the given element regardless of if it has attributes or not.
Below is how i execute it in TSQL:
DECLARE #generatedXPATH nvarchar(500),
#elementVal nvarchar(50),
#xml xml,
#query nvarchar(max)
-- it works with this payload
-- because attributes aren't there replacing it with xml
-- where element attributes are present fails the element value extraction using xpath
SET #xml = '<Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <sVerify> <verifyPost> <scope> <machine> <name>test</name> <space>test2</space> </machine> <Sys>internal</Sys> <Date>2013-02-28</Date> </scope> <Message> <Error>11111111111</Error> <Descrip>222222222</Descrip> </Message> <Final>true</Final> <Receipt>33333</Receipt> </verifyPost> </sVerify> </Body> </Envelope>'
SET #generatedXPATH = '/Envelope[1]/Body[1]/sVerify[1]/verifyPost[1]/scope[1]/machine[1]/space[1]'
SET #elementVal = ''
SET #query = N'SELECT #elementVal= Nodes.node.value(''(' + #generatedXPATH
+ ')[1]'', ''varchar(50)'') FROM #xml.nodes(''.'') AS Nodes(node)';
exec sp_ExecuteSql
#query,
N' #xml xml,#elementVal nvarchar(max) output',
#xml = #xml,
#elementVal = #elementVal output
select #elementVal
Update:
Seems like only the attributes where attribute is without prefix is causing the issue. for example if attribute is xlmns="..........." then i am not able to grab its value using XPATH, if attribute was xlmns:i=".........." then it seems to work. Not sure what's going on.
If you have a XML with a custom namespace, you need to define it and use it as an affix for every element that is defined under that namespace using WITH XMLNAMESPACES (http://technet.microsoft.com/en-us/library/ms177607.aspx) In your case, try this:
SET #generatedXPATH = '/Envelope[1]/Body[1]/ns:sVerify[1]/ns:verifyPost[1]/ns:scope[1]/ns:machine[1]/ns:space[1]'
SET #elementVal = ''
SET #query = N'WITH XMLNAMESPACES (''http://www.myCompany.com/Location/2014'' AS ns)
SELECT #elementVal= Nodes.node.value(''(' + #generatedXPATH
+ ')[1]'', ''varchar(50)'') FROM #xml.nodes(''.'') AS Nodes(node)';
Edit: If you do not know the namespace, using * instead of ns should work for getting the element regardless of namespace:
SET #generatedXPATH = '/Envelope[1]/Body[1]/*:sVerify[1]/*:verifyPost[1]/*:scope[1]/*:machine[1]/*:space[1]'
SET #elementVal = ''
SET #query = N'SELECT #elementVal= Nodes.node.value(''(' + #generatedXPATH
+ ')[1]'', ''varchar(50)'') FROM #xml.nodes(''.'') AS Nodes(node)';