How to decode & etc using XQuery/SQL? - sql

Using Microsoft SQL Server...
declare #x xml
set #x = '<Example><You & Me></Example>'
select cast(#x.query('/Example/text()') as nvarchar(50))
The result is "<You & Me>" rather than "<You & Me>".
How can I have SQL read the XML in such as way that '<', '&' and '>' are decoded?

Use value() instead of query().
declare #x xml
set #x = '<Example><You & Me></Example>'
select #x.value('(/Example)[1]', 'nvarchar(50)')
SQL-Fiddle

Related

How to add additional XML node on top of my SQL generated XML

I have generated XML from a SQL Server FOR XML PATH statement as shown here:
USE MySQLDB
SELECT *
FROM BillTable
FOR XML PATH ('BillAdd'), ROOT ('BillAddRq')
And this is the result:
<BillAddRq>
<BillAdd>
<TxnID>2432-1071510295</TxnID>
<TimeCreated>2003-12-16T01:44:55</TimeCreated>
<TimeModified>2015-12-15T22:38:33</TimeModified>
<EditSequence>1450190313</EditSequence>
<TxnNumber>413</TxnNumber>
<VendorRef_ListID>E0000-933272656</VendorRef_ListID>
<VendorRef_FullName>Timberloft Lumber</VendorRef_FullName>
<APAccountRef_ListID>C0000-933270541</APAccountRef_ListID>
<APAccountRef_FullName>Accounts Payable</APAccountRef_FullName>
<TxnDate>2016-12-01T00:00:00</TxnDate>
<DueDate>2017-12-31T00:00:00</DueDate>
<AmountDue>80.50000</AmountDue>
<TermsRef_ListID>50000-933272659</TermsRef_ListID>
<TermsRef_FullName>1% 10 Net 30</TermsRef_FullName>
<IsPaid>0</IsPaid>
</BillAdd>
<BillAdd>
<TxnID>243A-1071510389</TxnID>
<TimeCreated>2003-12-16T01:46:29</TimeCreated>
<TimeModified>2015-12-15T22:38:33</TimeModified>
<EditSequence>1450190313</EditSequence>
<TxnNumber>414</TxnNumber>
<VendorRef_ListID>C0000-933272656</VendorRef_ListID>
<VendorRef_FullName>Perry Windows & Doors</VendorRef_FullName>
<APAccountRef_ListID>C0000-933270541</APAccountRef_ListID>
<APAccountRef_FullName>Accounts Payable</APAccountRef_FullName>
<TxnDate>2016-12-02T00:00:00</TxnDate>
<DueDate>2018-01-01T00:00:00</DueDate>
<AmountDue>50.00000</AmountDue>
<TermsRef_ListID>10000-933272658</TermsRef_ListID>
<TermsRef_FullName>Net 30</TermsRef_FullName>
<IsPaid>0</IsPaid>
</BillAdd>
</BillAddRq>
Now, I'd like to encapsulate the above with these nodes:
<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="15.0"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
//above generated xml//
</QBXMLMsgsRq>
</QBXML>
How will I achieve this in a SQL Query I created above?
I am new to SQL Server and XML. I am trying to generate this XML directly from my database and vice versa to make it more efficient and faster — let my SQL directly communicate with XML.
ATTEMPT 1:
USE MySQLDB;
GO
DECLARE #myDoc XML;
SET #myDoc = '<QBXML>
<QBXMLMsgsRq onError="stopOnError">
</QBXMLMsgsRq>
</QBXML>';
SET #myDoc.modify('
insert
-- instead of inserting string here.. I would like to insert here the query I made above
into (/QBXML/QBXMLMsgsRq)[1]');
SELECT #myDoc;
ATTEMPT 2:
USE MySQLDB;
GO
DECLARE #myDoc XML;
SET #myDoc = '<QBXML>
<QBXMLMsgsRq onError="stopOnError">
</QBXMLMsgsRq>
</QBXML>';
DECLARE #qry XML;
SET #qry = (SELECT * FROM BillTable FOR XML PATH ('BillAdd'), ROOT ('BillAddRq'));
-- SELECT #qry;
SET #myDoc.modify('insert #qry
into (/QBXML/QBXMLMsgsRq)[1]');
SELECT #myDoc;
There are many ways to construct your XML result, consider the following three alternatives...
Use XML.modify() to insert the BillTable XML into an XML scalar variable (which includes the ?qbxml XML processing instruction):
declare #BillTableXml xml = (
select *
from BillTable
for xml path('BillAdd'), root('BillAddRq')
);
declare #myDoc xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="15.0"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
</QBXMLMsgsRq>
</QBXML>';
set #myDoc.modify('
insert sql:variable("#BillTableXml")
into (/QBXML/QBXMLMsgsRq)[1]
');
select #myDoc as Result;
Use a nested query to construct the entire XML result (which does not, however, include the ?qbxml XML processing instruction):
select
'stopOnError' as [QBXML/QBXMLMsgsRq/#onError],
(
select *
from BillTable
for xml path('BillAdd'), root('BillAddRq'), type
) as [QBXML/QBXMLMsgsRq]
for xml path('');
Or use an XQuery to construct the entire XML result (which also includes the ?qbxml XML processing instruction):
select BillTableXml.query('
<?qbxml version="15.0"?>,
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
{ /BillAddRq }
</QBXMLMsgsRq>
</QBXML>
') as Result
from (
select *
from BillTable
for xml path('BillAdd'), root('BillAddRq'), type
) Data (BillTableXml);

SQL - Define/Modify XML with variables

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>

Traversing Two XMLs in getting intersection of data in SQL

I have two xmls like below. i want to return the first xml in which ids are matched with second xml.
DECLARE
#x xml,
#y xml
SET #x = '<roots><root><id>1</id><name>rp</name></root><root><id>2</id><name>pr</name></root><root><id>3</id><name>rrp</name></root></roots>'
SET #y = '<root><id>1</id><id>5</id><id>6</id></root>'
output: <roots><root><id>1</id><name>rp</name></root></roots>
i am able to acheive this with loop. But is there any simple way to do this.
Shred #x on roots/root and extract the XML and the value for id.
Check against #y in the where clause using exist() and sql:column().
select X.root
from (
select X.X.query('*') as root,
X.X.value('(id/text())[1]', 'int') as id
from #x.nodes('/roots/root') as X(X)
)
as X
where #y.exist('/root/id/text()[. = sql:column("X.id")]') = 1
for xml path(''), root('roots')
Result:
<roots>
<root>
<id>1</id>
<name>rp</name>
</root>
</roots>

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.

Convert to XML having special character name in SQL server

There is a XML in the below format present in a column of datatype VARCHAR(MAX) in sql table
<?xml version="1.0" encoding="UTF-8"?>
<APIDATA xmlns="mynamespace">
<TRADE Action="Insert" CompanyID="33" Trader="Aleš Holubec"
</TRADE>
</APIDATA>
I need to fetch attribute values of Action and CompanyID . i.e, Insert and 33
In SQL, i used the below query
;WITH XMLNAMESPACES(DEFAULT 'mynamespace')
SELECT CONVERT(XML,column_name).value ('(APIDATA/TRADE/#Action)[1]', 'varchar(100)')
But i get the below error
XML parsing: line 1, character 537, illegal xml character
The reason is Aleš Holubec is the name of the trader in the XML which we cannot change.
Please help how to resolve this
You can fix this by converting your string to nvarchar(max) and then replace the encoding to UTF-16.
;WITH XMLNAMESPACES(DEFAULT 'mynamespace')
SELECT CONVERT(XML, replace(cast(column_name as nvarchar(max)), '<?xml version="1.0" encoding="UTF-8"?>', '<?xml version="1.0" encoding="UTF-16"?>')).value ('(APIDATA/TRADE/#Action)[1]', 'varchar(100)')
Or you can just remove the XML declaration.
;WITH XMLNAMESPACES(DEFAULT 'mynamespace')
SELECT CONVERT(XML, replace(column_name, '<?xml version="1.0" encoding="UTF-8"?>', '')).value ('(APIDATA/TRADE/#Action)[1]', 'varchar(100)')
I ran into the same issue with special characters and XML. Below is an example of what I did to resolve the problem with working code that you will need to modify for your DB.
The only thing I needed C# to handle for me was the single quote. C# will need to change the single quote to &apos; before sending it as a CSV list, if not it will fail.
Hope this helps..
/**************************************************************************************
**************************************************************************************
***
*** This is to demonstrate how to use XML in SQL statements.
*** C# must send the the list of items in comma seperated values and
*** replace the character Single Quote with &apos; or the SQL statement
*** will fail. This is the only character that SQL can not handle when sent as a
*** string.
***
*** The steps below will explain each part and how it handles special
*** characters in XML and turns them into a temp table.
***
***
**************************************************************************************
**************************************************************************************/
SET ARITHABORT ON
DECLARE #CUST_REF3 AS XML
DECLARE #CSharpString AS NVARCHAR(MAX)
--This is used for when the CSV string is passed in normally.
SET #CSharpString = ISNULL('{0}', '')
--Example of when the CSV string is passed in and it having a special characters.
SET #CSharpString = 'SHOP A, LA WHEEL & BRAKE SHOP, SHOP&apos;s B'
--Example of when nothing is passed in from C#
--SET #CSharpString = ''
--Replaces any special character for preperation of XML formating
SET #CSharpString = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(#CSharpString,'&', '&'),'<', '<'),'>', '>'),'"', '"'),'''', '&apos;')
-- Turn the #CSharpString (CSV) into XML
SET #CUST_REF3 = CAST('<A>'+ NULLIF(REPLACE(#CSharpString, N',', '</A><A>'), '') + '</A>' AS XML)
--Display the converted XML list
SELECT #CUST_REF3
--Converts the XML nodes into a temp table. Make sure the data type is set correctly for the data being received.
SELECT dbo.Trim(CUST_REF3.value('.','nchar(22)')) AS [CUST_REF3]
INTO #XML_List
FROM #CUST_REF3.nodes('/A') AS x(CUST_REF3)
--Display what Converted list
SELECT * FROM #XML_List
--example of output from the C# String
SELECT USER_DOC, CUST_REF3
FROM dbo.WO_HDR
WHERE
--This matches the list of items sent from C#
((SELECT TOP(1) x.CUST_REF3
FROM #XML_List x
WHERE x.CUST_REF3 = dbo.WO_HDR.CUST_REF3) IS NOT NULL OR
--If the user did not select anything, do not filter
#CSharpString = '')
--Dispose of temp table
IF OBJECT_ID('tempdb..#XML_List') IS NOT NULL
DROP TABLE #XML_List
/**********************************************************************************************************************************************************
--Summerized code to use:
SET ARITHABORT ON
DECLARE #CUST_REF3 AS XML
DECLARE #CSharpString AS NVARCHAR(MAX)
SET #CSharpString = ISNULL('{0}', '')
SET #CSharpString = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(#CSharpString,'&', '&'),'<', '<'),'>', '>'),'"', '"'),'''', '&apos;')
SET #CUST_REF3 = CAST('<A>'+ NULLIF(REPLACE(#CSharpString, N',', '</A><A>'), '') + '</A>' AS XML)
SELECT dbo.Trim(CUST_REF3.value('.','nchar(22)')) AS [CUST_REF3] INTO #XML_List FROM #CUST_REF3.nodes('/A') AS x(CUST_REF3)
--Add this scring to every WHERE statment you want this filter to be used.
--((SELECT TOP(1) x.CUST_REF3 FROM #XML_List x WHERE x.CUST_REF3 = dbo.WO_HDR.CUST_REF3) IS NOT NULL OR #CSharpString = '')
IF OBJECT_ID('tempdb..#XML_List') IS NOT NULL DROP TABLE #XML_List
**********************************************************************************************************************************************************/