Extract values from XML String in oracle - sql

How do you extract the value of an xml string in oracle e.g.
XML Example
<?xml version="1.0" encoding="UTF-8"?>
<node>
<key label="name">Test</key>
<key label="lastname">Test</key>
</node>
So far this is what I have tried to extract the values
SELECT <COLUMN>, EXTRACTVALUE(xmltype(<COLUMN>),'/node/lastname') AS TEST FROM <TABLE>;
But it always returns a null value.

Related

Add data to result of FOR XML PATH SQL SERVER

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')

Stripping data from xml in SQL Server

One of my tables with xml datatype has the following xml information:
<RequestMetaData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MetaData Type="DocImport">
<Keywords>
<Key Name="Zone" Value="MIO" />
<Key Name="ClassificationStrategy" Value="NeedClassification" />
<Key Name="Folder" Value="0456e6ca" />
</Keywords>
</MetaData>
<MetaData Type="SourceResponse">
<Keywords>
<Key Name="NotificationResponse_20180427-150426" Value="Received successful response from Source" />
</Keywords>
</MetaData>
</RequestMetaData>
I need to write an SQL query to fetch the value of Classification strategy based on key name.
I have added the xml in a variable #xml and used the following code. It is returning NULL.
select A.b.value('ClassificationStrategy[1]', 'VARCHAR(30)') AS CS
FROM #xml.nodes('/RequestMetaData/MetaData/Keywords') AS A(b)
Can someone please help me with this.
You can read your XML in various ways. Use a simple .value() with an XPath/XQuery expression to retrieve a single value, use .query to retrieve a part of the XML or use .nodes() to return repeated elements as derived table:
DECLARE #xml XML=
N'<RequestMetaData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MetaData Type="DocImport">
<Keywords>
<Key Name="Zone" Value="MIO" />
<Key Name="ClassificationStrategy" Value="NeedClassification" />
<Key Name="Folder" Value="0456e6ca" />
</Keywords>
</MetaData>
<MetaData Type="SourceResponse">
<Keywords>
<Key Name="NotificationResponse_20180427-150426" Value="Received successful response from Source" />
</Keywords>
</MetaData>
</RequestMetaData>';
--Read the whole lot
SELECT md.value('#Type','nvarchar(max)') AS MetaDataType
,k.value('#Name','nvarchar(max)') AS KeyName
,k.value('#Value','nvarchar(max)') AS KeyValue
FROM #xml.nodes('/RequestMetaData/MetaData') A(md)
OUTER APPLY md.nodes('Keywords/Key') B(k);
--Get one key's value by name (anywhere in the doc)
DECLARE #keyName VARCHAR(100)='ClassificationStrategy';
SELECT #xml.value('(//Key[#Name=sql:variable("#keyName")]/#Value)[1]','nvarchar(max)');
--Use the meta data type as additional filter (if key names are not unique per doc)
DECLARE #kName VARCHAR(100)='ClassificationStrategy';
DECLARE #mdType VARCHAR(100)='DocImport';
SELECT #xml.value('(/RequestMetaData
/MetaData[#Type=sql:variable("#mdType")]
/Keywords
/Key[#Name=sql:variable("#kName")]
/#Value)[1]','nvarchar(max)');

Save XML with attribute to Table in SQL Server

Hi I have XML data with attribute as input for SQL, i need this to be inserted in my table.
XML Data is
<?xml version="1.0" encoding="ISO-8859-1"?>
<MESSAGEACK>
<GUID GUID="kfafb30" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="1" CODE="28681" />
</GUID>
<GUID GUID="kfafb3" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="2" CODE="286381" />
</GUID>
</MESSAGEACK>
I want this to be inserted in below Format
GUID SUBMIT DATE ID ERROR SEQ CODE
kfafb3 2015-10-15 11:30:29 1 1 28681
kfafb3 2015-10-15 11:30:29 1 1 2868
please help.
Look into XPath and xml Data Type Methods in MSDN. This is one possible way :
declare #xml As XML = '...you XML string here...'
INSERT INTO YourTable
SELECT
guid.value('#GUID', 'varchar(100)') as 'GUID'
,guid.value('#SUBMITDATE', 'datetime') as 'SUBMIT DATE'
,guid.value('#ID', 'int') as 'ID'
,guid.value('ERROR[1]/#SEQ', 'int') as 'SEQ'
,guid.value('ERROR[1]/#CODE', 'int') as 'CODE'
FROM #xml.nodes('/MESSAGEACK/GUID') as x(guid)
Result :
just paste this into an empty query window and execute. Adapt to your needs:
DECLARE #xml XML=
'<?xml version="1.0" encoding="ISO-8859-1"?>
<MESSAGEACK>
<GUID GUID="kfafb30" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="1" CODE="28681" />
</GUID>
<GUID GUID="kfafb3" SUBMITDATE="2015-10-15 11:30:29" ID="1">
<ERROR SEQ="2" CODE="286381" />
</GUID>
</MESSAGEACK>';
SELECT Msg.Node.value('#GUID','varchar(max)') AS [GUID] --The value is no GUID, if the original values are, you could use uniqueidentifier instead of varchar(max)
,Msg.Node.value('#SUBMITDATE','datetime') AS SUBMITDATE
,Msg.Node.value('#ID','int') AS ID
,Msg.Node.value('(ERROR/#SEQ)[1]','int') AS [ERROR SEQ]
,Msg.Node.value('(ERROR/#CODE)[1]','int') AS CODE
FROM #xml.nodes('/MESSAGEACK/GUID') AS Msg(Node)

How can I query a value in SQL Server TEXT column that contains XML (not xml column type)

I have table DOCUMENTS with:
DOCUMENTS
____________________
DOCUMENTID int
USERID int
CONTENT text
I have following XML stored in TEXT column with name CONTENT in a SQL Server database
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IDMSDocument>
<DocumentContent>
<Attribute Name="Number" GUID="{FFFFFFFF-0000-0000-0000-000000000001}">
<IDMSType>3060</IDMSType>
<Value Type="Integer">
<Value>122</Value>
</Value>
</Attribute>
<Attribute Name="Date" GUID="{FFFFFFFF-0000-0000-0000-000000000002}">
<IDMSType>3061</IDMSType>
<Value Type="DateTime">
<Date>10-09-2014</Date>
</Value>
</Attribute>
<Attribute Name="Публикуване" GUID="{CA646F55-5229-4FC5-AA27-494B25023F4E}">
<IDMSType>3062</IDMSType>
<Value Type="String">
<Value>Да</Value>
</Value>
</Attribute>
<Attribute Name="Дата" GUID="{AC0465B0-4FB4-49E2-B4FA-70901068FD9B}">
<IDMSType>3063</IDMSType>
<Value Type="DateTime">
<Date>01-10-2014</Date>
</Value>
</Attribute>
<Attribute Name="Предмет на поръчка" GUID="{04A4EC72-6F33-461F-98DD-D8D271997788}">
<IDMSType>3065</IDMSType>
<Value Type="String">
<Value>Избор на консултанти</Value>
</Value>
</Attribute>
</DocumentContent>
</IDMSDocument>
I find a way how to query dingle XML attribute in a single row from table, with heavy conversion :
DECLARE #strContent NVARCHAR(MAX);
DECLARE #xmlContent XML;
SET #strContent = (select content from DOCUMENTS where DocumentID=24);
SET #xmlContent = CAST(REPLACE(CAST(#strContent AS NVARCHAR(MAX)),'utf-8','utf-16') AS XML)
SELECT #xmlContent.query('/IDMSDocument/DocumentContent/Attribute[5]/Value/Value')
where #xmlContent.value('(/IDMSDocument/DocumentContent/Attribute[5]/Value/Value)[1]', 'nvarchar(max)') like '%search%'
I need to make query like "select all rows in table that in fifth attribute in XML have value 'search' "
For me the general problem is that column type is not XML, but I have text column with stored xml inside. when I try cast, query, value directly server return that I can use it Only with XML column.
I would be very grateful if someone suggest how to do this!
Thanks!
Kamen
SQL Server doesn't allow inline XML conversion to have functions applied, i.e.: no CAST(...).query(), use a CTE to handle the conversion:
;WITH cte AS (
SELECT DocumentID, UserID, CAST(Content AS XML) AS XMLContent
FROM Documents
)
SELECT XMLContent.query('/IDMSDocument/DocumentContent/Attribute[5]/Value/Value')
FROM cte
WHERE XMLContent.value('(/IDMSDocument/DocumentContent/Attribute[5]/Value/Value)[1]', 'nvarchar(max)') like '%search%'
One thing though: I saw Unicode (Russian?) characters in the Content column. It may be better to use utf-16 in your XML and ntext for column type.
text and ntext are also on the way out. If this is new code, use nvarchar(max)

TSQL XML Parsing and creating xml

I have a tool which I now will be creating reports for using the data I have. I am currently working on a year to date report and need to pull the numbers for that.
My goal is to have an XML output of each of the months in the current year with their totals.
Here is what the XML currently looks like with my select statement:
<root>
<data>
<classXML>
<courses>
<class>
<classTitle>Arts and Crafts</classTitle>
<tuitionCost>100</tuitionCost>
<bookCost>30</bookCost>
<classTotal>130</classTotal>
</class>
<class>
<classTitle>Paper 101</classTitle>
<tuitionCost>320</tuitionCost>
<bookCost>211</bookCost>
<classTotal>531</classTotal>
</class>
<class>
<classTitle>Introduction to Pencils</classTitle>
<tuitionCost>210</tuitionCost>
<bookCost>291</bookCost>
<classTotal>501</classTotal>
</class>
<class>
<classTitle>Intermediate Folding</classTitle>
<tuitionCost>110</tuitionCost>
<bookCost>22</bookCost>
<classTotal>132</classTotal>
</class>
<class>
<classTitle>Advanced Jumprope</classTitle>
<tuitionCost>11</tuitionCost>
<bookCost>22</bookCost>
<classTotal>33</classTotal>
</class>
<grandTotal>1327</grandTotal>
</courses>
</classXML>
<reimbursementDate>08/01/2014</reimbursementDate>
</data>
<data>
<classXML>
<courses>
<class>
<classTitle>dsfgfdsg</classTitle>
<tuitionCost>44</tuitionCost>
<bookCost>44</bookCost>
<classTotal>88</classTotal>
</class>
<grandTotal>88</grandTotal>
</courses>
</classXML>
<reimbursementDate>05/31/2014</reimbursementDate>
</data>
</root>
And my stored procedure:
SELECT
A.[classXML],
CONVERT(VARCHAR(10), A.[reimbursementDate], 101) as reimbursementDate
FROM
tuitionSubmissions as A
WHERE
A.[status] = 'Approved'
AND YEAR(A.[reimbursementDate]) = YEAR(GETDATE())
FOR XML PATH ('data'), TYPE, ELEMENTS, ROOT ('root');
As you can see, the column classXML stores that data in XML format with all of the classes they are enrolled in with their costs.
So I need to loop over the XML and create an output that is just numbers to assist with my reporting.
Here is my desired outcome:
<results>
<dataSet>
<month>8</month>
<year>2014</year>
<tuitionTotal>500</tuitionTotal>
<booksTotal>200</booksTotal>
<grandTotal>700</grandTotal>
</dataSet>
<dataSet>
<month>9</month>
<year>2014</year>
<tuitionTotal>100</tuitionTotal>
<booksTotal>500</booksTotal>
<grandTotal>600</grandTotal>
</dataSet>
</results>
You can use sum Function (XQuery) to do the aggregation against your XML column.
I put the query against the XML in a cross apply so you don't have to do the same XQuery twice just to calculate grandTotal.
You should also change your predicate against reimbursementDate so it may use and index to find the rows.
select datepart(month, T.reimbursementDate) as month,
datepart(year, T.reimbursementDate) as year,
S.tuitionTotal,
S.booksTotal,
S.tuitionTotal + S.booksTotal as grandTotal
from dbo.tuitionSubmissions as T
cross apply (
select T.classXML.value('sum(/courses/class/tuitionCost/text())', 'int') as tuitionTotal,
T.classXML.value('sum(/courses/class/bookCost/text())', 'int') as booksTotal
) as S
where T.status = 'Approved' and
T.reimbursementDate >= '20140101' and
T.reimbursementDate < '20150101'
for xml path('dataSet'), root('results'), type
SQL Fiddle
DECLARE #DocH INT
DECLARE #DOC XML = '
<root>
<data>
<classXML>
<courses>
<class>
<classTitle>Arts and Crafts</classTitle>
<tuitionCost>100</tuitionCost>
<bookCost>30</bookCost>
<classTotal>130</classTotal>
</class>
<class>
<classTitle>Paper 101</classTitle>
<tuitionCost>320</tuitionCost>
<bookCost>211</bookCost>
<classTotal>531</classTotal>
</class>
<class>
<classTitle>Introduction to Pencils</classTitle>
<tuitionCost>210</tuitionCost>
<bookCost>291</bookCost>
<classTotal>501</classTotal>
</class>
<class>
<classTitle>Intermediate Folding</classTitle>
<tuitionCost>110</tuitionCost>
<bookCost>22</bookCost>
<classTotal>132</classTotal>
</class>
<class>
<classTitle>Advanced Jumprope</classTitle>
<tuitionCost>11</tuitionCost>
<bookCost>22</bookCost>
<classTotal>33</classTotal>
</class>
<grandTotal>1327</grandTotal>
</courses>
</classXML>
<reimbursementDate>08/01/2014</reimbursementDate>
</data>
<data>
<classXML>
<courses>
<class>
<classTitle>dsfgfdsg</classTitle>
<tuitionCost>44</tuitionCost>
<bookCost>44</bookCost>
<classTotal>88</classTotal>
</class>
<grandTotal>88</grandTotal>
</courses>
</classXML>
<reimbursementDate>05/31/2014</reimbursementDate>
</data>
</root>'
EXEC sp_xml_preparedocument #DocH OUTPUT, #DOC
SELECT
MONTH(reimbursementDate) AS month
, YEAR(reimbursementDate) AS year
, SUM(tuitionCost) AS tuitionTotal, SUM(bookCost) AS bookTotal, SUM(tuitionCost+bookCost) AS grandTotal
FROM OPENXML(#DocH,'/root/data/classXML/courses/class') WITH (
classTitle varchar(40) 'classTitle'
, tuitionCost INT 'tuitionCost'
, bookCost INT 'bookCost'
, reimbursementDate date '../../../reimbursementDate'
)
GROUP BY MONTH(reimbursementDate)
, YEAR(reimbursementDate)
FOR XML PATH ('dataset')
EXEC sp_xml_removedocument #DocH;