Add data to result of FOR XML PATH SQL SERVER - sql

I have a query that produces XML, exactly as I want it:
SELECT *
FROM [staging].[vw_mydata]
FOR XML PATH('DOCUMENT')
This code produces this output:
<DOCUMENT>
<BRON>Bulk Import</BRON>
<INDEXEERDATUM>2015-07-30</INDEXEERDATUM>
<ITEMVALUE1>ITEMVALUE1</ITEMVALUE1>
<ITEMVALUE2>ITEMVALUE2</ITEMVALUE2>
<ITEMVALUE3>ITEMVALUE3</ITEMVALUE3>
<ITEMVALUE4>ITEMVALUE4</ITEMVALUE4>
<ITEMVALUE5>ITEMVALUE5</ITEMVALUE5>
<ITEMVALUE6>2015-07-30</ITEMVALUE6>
<ITEMVALUE7>ITEMVALUE7</ITEMVALUE7>
<ITEMVALUE8></ITEMVALUE8>
<ITEMVALUE9></ITEMVALUE9>
<ITEMVALUE10></ITEMVALUE10>
<ITEMVALUE11>ITEMVALUE11</ITEMVALUE11>
</DOCUMENT>
<BRON>Bulk Import</BRON>
<INDEXEERDATUM>2015-07-30</INDEXEERDATUM>
<ITEMVALUE1>ITEMVALUE1</ITEMVALUE1>
<ITEMVALUE2>ITEMVALUE2</ITEMVALUE2>
<ITEMVALUE3>ITEMVALUE3</ITEMVALUE3>
<ITEMVALUE4>ITEMVALUE4</ITEMVALUE4>
<ITEMVALUE5>ITEMVALUE5</ITEMVALUE5>
<ITEMVALUE6>2015-07-30</ITEMVALUE6>
<ITEMVALUE7>ITEMVALUE7</ITEMVALUE7>
<ITEMVALUE8></ITEMVALUE8>
<ITEMVALUE9></ITEMVALUE9>
<ITEMVALUE10></ITEMVALUE10>
<ITEMVALUE11>ITEMVALUE11</ITEMVALUE11>
</DOCUMENT>
However, I need to add information before and after this result.
The following has to preceed the results from the query. This part is fixed and does not change.
<?xml version="1.0"?>
<DOCUMENTS>
<VERSION>2.0</VERSION>
<LICENTIEHOUDER>Henrov</LICENTIEHOUDER>
<XTN>BulkImport</XTN>
<ARCHIEFNAAM>Name</ARCHIEFNAAM>
<ITEMLABEL1>Label1</ITEMLABEL1>
<ITEMLABEL2>Label2</ITEMLABEL2>
<ITEMLABEL3>Label3</ITEMLABEL3>
<ITEMLABEL4>Label4</ITEMLABEL4>
<ITEMLABEL5>Label5</ITEMLABEL5>
<ITEMLABEL6>Label6</ITEMLABEL6>
<ITEMLABEL7>Label7</ITEMLABEL7>
<ITEMLABEL8>Label8</ITEMLABEL8>
<ITEMLABEL9>Label9</ITEMLABEL9>
<ITEMLABEL10>Label0</ITEMLABEL10>
<ITEMLABEL11>Label1</ITEMLABEL11>
<ITEMLABEL12>Label2</ITEMLABEL12>
<ITEMLABEL13>Label3</ITEMLABEL13>
<ITEMLABEL14>Label4</ITEMLABEL14>
<ITEMLABEL15>Label5</ITEMLABEL15>
<ITEMLABEL16>Label6</ITEMLABEL16>
<ITEMLABEL17>Label7</ITEMLABEL17>`
</DOCUMENTS> needs to be added to the end so that the whole result looks like this:
<?xml version="1.0"?>
<DOCUMENTS>
<VERSION>2.0</VERSION>
<LICENTIEHOUDER>Henrov</LICENTIEHOUDER>
<XTN>BulkImport</XTN>
<ARCHIEFNAAM>Name</ARCHIEFNAAM>
<ITEMLABEL1>Label1</ITEMLABEL1>
<ITEMLABEL2>Label2</ITEMLABEL2>
<ITEMLABEL3>Label3</ITEMLABEL3>
<ITEMLABEL4>Label4</ITEMLABEL4>
<ITEMLABEL5>Label5</ITEMLABEL5>
<ITEMLABEL6>Label6</ITEMLABEL6>
<ITEMLABEL7>Label7</ITEMLABEL7>
<ITEMLABEL8>Label8</ITEMLABEL8>
<ITEMLABEL9>Label9</ITEMLABEL9>
<ITEMLABEL10>Label0</ITEMLABEL10>
<ITEMLABEL11>Label1</ITEMLABEL11>
<ITEMLABEL12>Label2</ITEMLABEL12>
<ITEMLABEL13>Label3</ITEMLABEL13>
<ITEMLABEL14>Label4</ITEMLABEL14>
<ITEMLABEL15>Label5</ITEMLABEL15>
<ITEMLABEL16>Label6</ITEMLABEL16>
<ITEMLABEL17>Label7</ITEMLABEL17>
<DOCUMENT>
<BRON>Bulk Import</BRON>
<INDEXEERDATUM>2015-07-30</INDEXEERDATUM>
<ITEMVALUE1>ITEMVALUE1</ITEMVALUE1>
<ITEMVALUE2>ITEMVALUE2</ITEMVALUE2>
<ITEMVALUE3>ITEMVALUE3</ITEMVALUE3>
<ITEMVALUE4>ITEMVALUE4</ITEMVALUE4>
<ITEMVALUE5>ITEMVALUE5</ITEMVALUE5>
<ITEMVALUE6>2015-07-30</ITEMVALUE6>
<ITEMVALUE7>ITEMVALUE7</ITEMVALUE7>
<ITEMVALUE8></ITEMVALUE8>
<ITEMVALUE9></ITEMVALUE9>
<ITEMVALUE10></ITEMVALUE10>
<ITEMVALUE11>ITEMVALUE11</ITEMVALUE11>
</DOCUMENT>
<BRON>Bulk Import</BRON>
<INDEXEERDATUM>2015-07-30</INDEXEERDATUM>
<ITEMVALUE1>ITEMVALUE1</ITEMVALUE1>
<ITEMVALUE2>ITEMVALUE2</ITEMVALUE2>
<ITEMVALUE3>ITEMVALUE3</ITEMVALUE3>
<ITEMVALUE4>ITEMVALUE4</ITEMVALUE4>
<ITEMVALUE5>ITEMVALUE5</ITEMVALUE5>
<ITEMVALUE6>2015-07-30</ITEMVALUE6>
<ITEMVALUE7>ITEMVALUE7</ITEMVALUE7>
<ITEMVALUE8></ITEMVALUE8>
<ITEMVALUE9></ITEMVALUE9>
<ITEMVALUE10></ITEMVALUE10>
<ITEMVALUE11>ITEMVALUE11</ITEMVALUE11>
</DOCUMENT>
</DOCUMENTS>
This needs to be exported as a XML file.
I tried to do SELECT 'text in front' + (select for xml) + 'text after' but that has an unwanted result: all < and > are replaced with &lt en &gt effectively destroying my xml.
So basically: how do I add information to the beginning and the end of the result of a FOR XML query?

Simply create a sub query for documents. You'll need to use type to prevent XML tags escaping.
SELECT
'2.0' AS [VERSION],
'Henrov' AS [LICENTIEHOUDER],
-- Add all other fields.
(
SELECT *
FROM [staging].[vw_mydata]
FOR XML PATH('DOCUMENT'), type
)
FOR XML PATH('DOCUMENTS')

Related

Import field in XML using SQL not working

i've been trying to import the field GivenName in my example XML but for some reason it's not working, i've been using the following SQL query, i think i'm using the correct field and the correct nodes but i'm not 100% sure about the XMLNameSpaces
Thank you very much in advance for your help
This is the example SQL Query i'm using:
DECLARE #xml XML = (SELECT [Xml] FROM ExampleTable)
;WITH XMLNAMESPACES (DEFAULT 'http://www.opentravel.org/OTA/2003/05','http://www.w3.org/2003/05/soap-envelope' )
select FirstName = ProfileInfo.value('Profiles[1]/ProfileInfo[1]/Profile[1]/Customer[1]/PersonName[1]/#GivenName', 'nvarchar(255)')
FROM #xml.nodes('Envelope/Body/OTA_Example/Info/Infos/ResUser') as T1(Profiles)
outer apply T1.Profiles.nodes('ResUser2') as T2(ProfileInfo)
This is the example XML i'm using for the import:
<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">
<soap2:Header xmlns:htng="http://htng.org/1.3/Header/" xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:wss="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:htnga="http://htng.org/PWSWG/2007/02/AsyncHeaders"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soap2="http://www.w3.org/2003/05/soap-envelope">
<wsa:Action>Example</wsa:Action>
<wsa:ReplyTo>
<wsa:Address>Example2</wsa:Address>
</wsa:ReplyTo>
<htnga:ReplyTo>
<wsa:Address>Example3</wsa:Address>
</htnga:ReplyTo>
<wsa:MessageID>123</wsa:MessageID>
</soap2:Header>
<Body>
<OTA_Example xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.opentravel.org/OTA/2003/05" Version="5.000" >
<Info>
<Infos CreateDateTime="2021-09-20T06:52:40" CreatorID="User">
<UniqueID Type="1" ID="12341251" />
<ResUser>
<ResUser2 ResGuestRPH="1" PrimaryIndicator="true">
<Profiles>
<ProfileInfo>
<Profile ProfileType="1">
<Customer>
<PersonName>
<NamePrefix>Mr.</NamePrefix>
<GivenName>FirstnameTest</GivenName>
<Surname>LastnameTest</Surname>
</PersonName>
</Customer>
</Profile>
</ProfileInfo>
</Profiles>
</ResUser2>
</ResUser>
</Infos>
</Info>
</OTA_Example>
</Body>
</Envelope>
GivenName is not an attribute, so you shouldn't use # for it.
It's unclear why you needed .nodes, it is only needed if there were multiple nodes that needed breaking out into separate rows
You can also select straight out of ExampleTable, you do not need to store it in a variable.
;WITH XMLNAMESPACES (
'http://www.w3.org/2003/05/soap-envelope' AS soap,
DEFAULT 'http://www.opentravel.org/OTA/2003/05')
select FirstName = [XML].value('(soap:Envelope/soap:Body/OTA_Example/Info/Infos/ResUser/ResUser2/Profiles/ProfileInfo/Profile/Customer/PersonName/GivenName/text())[1]', 'nvarchar(255)')
FROM ExampleTable
db<>fiddle

SQL query for XML data

I have a SQL Server database table with a column called XML that contains XML data which is structured like this:
<Item xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://test/data">
<Roots>
<Root>
<Name>Field Name</Name>
<Value>Field Value</Value>
</Root>
<Root>
<Name>Field Name</Name>
<Value>Field Value</Value>
</Root>
</Roots>
I want to use T-SQL to get the Value where Name = Total. I have tried the following but it isn't returning any data:
SELECT [XML]
FROM [BusinessAccount]
WHERE [XML].value('(/Root/Name)[13]', 'VARCHAR(MAX)') LIKE '%Total%'
If anyone could tell me where I've gone wrong?
You are missing the required WITH XMLNAMESPACES for your XML and the path is incorrect.
If you want to bring back rows where the 13th element consists of the text Total you can use the below.
WITH XMLNAMESPACES (DEFAULT 'http://test/data')
SELECT [XML]
FROM [BusinessAccount]
WHERE 1 = [XML].exist('(/Item/Roots/Root/Name)[13][text() = "Total"]')
Otherwise you can add the WITH XMLNAMESPACES to your original query and fix the path there too.
You need to specify namespaces. You can then match <Name> and <Value> pairs and extract the contents of <Value> like so:
SELECT NameNode.value('declare namespace x="http://test/data"; (../x:Value)[1]', 'varchar(100)')
FROM [BusinessAccount]
CROSS APPLY [XML].nodes('declare namespace x="http://test/data"; //x:Root/x:Name') AS n(NameNode)
WHERE NameNode.value('.', 'varchar(100)') = 'Total'
Demo on db<>fiddle

Parse XML text to identify the node values

I'm trying to parse an XML file that I get from a url (sample below) and I need to parse the items that are on the record node. I am planning to put it into a SQL database with code at the bottom but can't figure out that line of code
<pmcids status="ok">
<request idtype="pmid" pmids="" versions="yes" showaiid="no">
<echo>ids=19240239;tool=HCC;email=morgenxxx%40xxxx.edu;format=xml</echo>
</request>
<record requested-id="19240239" pmcid="PMC2668929" pmid="19240239" doi="10.1158/1055-9965.EPI-08-0866">
<versions><version pmcid="PMC2668929.1" mid="NIHMS104698" current="true"/>
</versions>
</record>
</pmcids>
SQL code:
nref.value('#PMID[1]','varchar(max)') pmid,
nref.value('#PMCID[1]','varchar(max)') PMCID
All help is appreciated. I hope that this is enough information to determine the correct syntax
Use the native XQuery support in SQL Server! Much simpler than OPENXML ....
Try this:
DECLARE #input XML = '<pmcids status="ok">
<request idtype="pmid" pmids="" versions="yes" showaiid="no">
<echo>ids=19240239;tool=HCC;email=morgenxxx%40xxxx.edu;format=xml</echo>
</request>
<record requested-id="19240239" pmcid="PMC2668929" pmid="19240239" doi="10.1158/1055-9965.EPI-08-0866">
<versions>
<version pmcid="PMC2668929.1" mid="NIHMS104698" current="true"/>
</versions>
</record>
</pmcids>'
SELECT
RequestedId = xc.value('#requested-id', 'int'),
pmcid = xc.value('#pmcid', 'varchar(50)'),
pmid = xc.value('#pmid', 'int'),
doi = xc.value('#doi', 'varchar(50)')
FROM
#input.nodes('/pmcids/record') AS XT(XC)
Basically, the .nodes() call returns a "virtual" table XT with a column XC that contains the XML fragment for each of the XML nodes that match your XPath expression - here a list of all <record> nodes under the <pmcids> root node.
Then, using the .value() call, you can "reach into" each of those nodes in the XML elements and retrieve the individual bits - since those are all attributes, you the # prefix to indicate an attribute, and define the data type of your attribute.
This gives me an output of:
which you could easily insert into a database table
Update: if you also need the mid from the <version> node - use this:
SELECT
RequestedId = xc.value('#requested-id', 'int'),
pmcid = xc.value('#pmcid', 'varchar(50)'),
pmid = xc.value('#pmid', 'int'),
doi = xc.value('#doi', 'varchar(50)'),
VersionPmcid = xver.value('#pmcid', 'varchar(50)'),
mid = xver.value('#mid', 'varchar(50)')
FROM
#input.nodes('/pmcids/record') AS XT(XC)
CROSS APPLY
XC.nodes('versions/version') AS XT2(XVer)
(I added the pmcid attribute from the <version> node, since there might be multiple <version> nodes under a <record> from what this sample looks like)

adding encoding information to the result of FOR XML [duplicate]

This question already has answers here:
SQL Server FOR XML Enclosing Element?
(2 answers)
Closed 7 years ago.
I have a Script, which returns a XML using FOR XML in SQL 2008. Is there any way to add the version and encoding information in the beginning of the output. Eventually, i am planning to save the output in a file.
For example, right now my output looks like this
<Agents>
<Agent id="1">
<Name>Mike</Name>
<Location>Sanfrancisco</Location>
</Agent>
<Agent id="2">
<Name>John</Name>
<Location>NY</Location>
</Agent>
</Agents>
I would like to append the line <?xml version="1.0" encoding="UTF-8"?> in the beginning of the Xml output
So i want the output something like
<?xml version="1.0" encoding="UTF-8"?>
<Agents>
<Agent id="1">
<Name>Mike</Name>
<Location>Sanfrancisco</Location>
</Agent>
<Agent id="2">
<Name>John</Name>
<Location>NY</Location>
</Agent>
As #gbn points out in another answer and on another question, "the XML data is stored internally as ucs-2", and SQL Server doesn't include it when producing the data. However, you can convert the XML to a string and append the XML declaration at the beginning manually. However, simply using UTF-8 in the declaration would be inaccurate. The Unicode string which SQL produces is in UCS-2. For example, this will fail:
SELECT CONVERT(xml,N'<?xml version="1.0" encoding="UTF-8"?>' + CONVERT(NVARCHAR(MAX),CONVERT(XML,N'<x>' + NCHAR(10176) + N'</x>')));
with error:
Msg 9402, Level 16, State 1, Line 1 XML parsing: line 1, character 38,
unable to switch the encoding
This, on the other hand, will work as expected:
SELECT CONVERT(xml,N'<?xml version="1.0" encoding="UCS-2"?>' + CONVERT(NVARCHAR(MAX),CONVERT(XML,N'<x>' + NCHAR(10176) + N'</x>')));
Here is code which will produce the full, declaration-laden XML string you seek for your example data:
DECLARE #Agents TABLE
(
AgentID int,
AgentName nvarchar(50),
AgentLocation nvarchar(100)
);
INSERT INTO #Agents (AgentID, AgentName, AgentLocation) VALUES (1, N'Mike', N'Sanfrancisco');
INSERT INTO #Agents (AgentID, AgentName, AgentLocation) VALUES (2, N'John', N'NY');
WITH BaseData AS
(
SELECT
(
SELECT
AgentID AS '#id',
AgentName AS 'Name',
AgentLocation AS 'Location'
FROM #Agents
FOR XML PATH('Agent'), ROOT('Agents'), TYPE
) AS AgentXML
), FullStringTable AS
(
SELECT
*,
'<?xml version="1.0" encoding="UCS-2"?>' +
CONVERT(nvarchar(max),AgentXML) AS FullString
FROM BaseData
)
SELECT
AgentXML AS OriginalXML,
FullString,
CONVERT(xml,FullString) AS FullStringConvertedToXML
FROM FullStringTable;
SQL Server internally always uses utf-16 ucs-2 so you could just append it like we did. That is, SQL Server would never generate anything with "utf-8".
Edit: after some digging:
http://www.devnewsgroups.net/group/microsoft.public.sqlserver.xml/topic60022.aspx
http://forums.asp.net/t/1455808.aspx
If you will not be attempting to manipulate the results as TSQL XML then the simplest thing to do will be create a varchar with the string you wish to append then add the XML to it using a CAST statemnt to convert the XML to varchar.
declare #testXML as XML
declare #testPrefix as varchar(255)
set #testPrefix = '<?xml version="1.0" encoding="UTF-8"?>'
set #testXML = '<Agents> <Agent id="1"> <Name>Mike</Name> <Location>Sanfrancisco</Location> </Agent> <Agent id="2"> <Name>John</Name> <Location>NY</Location> </Agent></Agents>'
select #testPrefix
Select #testXML
select #testPrefix + CAST(#testXML as varchar(max))

Modify xml element name in SQL Server

How to change element name from Cust to Customer?
<Cust id="1">
<Name>aaaaaaaaaa</Name>
<Desc>bbbbbbbbbb</Desc>
</Cust>
When I'm using following statement
select #myXml.query('/node()[1]/node()') for xml raw('Customer')
sql removes attributes
<Customer>
<Name>aaaaaaaaaa</Name>
<Desc>bbbbbbbbbb</Desc>
</Customer>
Try this:
SELECT
#myXml.value('(/Cust/#id)[1]', 'int') AS '#id',
#myXml.query('/node()[1]/node()')
FOR XML PATH('Customer')
Gives me an output of:
<Customer id="1">
<Name>aaaaaaaaaa</Name>
<Desc>bbbbbbbbbb</Desc>
</Customer>
With the FOR XML PATH, you can fairly easily "restore" that attribute that gets lost in the conversion.
You could use replace:
replace(replace(#YourXml, '<Cust id', '<Customer id)', '</Cust>', '</Customer>')
This is fairly safe, as < is not valid as data in XML, it would appear as < or an ASCII or UNICODE sequence.