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;
Related
I have the XML below in a column. I need to get to \Report\Criterias\Criteria (where name="Advertisers")\Elements\Element(where name="ListViewAvailable"). From here I need to list all the numbers that are in the Value element.
So far I got:
SELECT xmlColumn.query('/Report/Criterias/Criteria/Elements/Element')
from tbl
but no idea how to filter.
<Report>
<Criterias>
<Criteria name="Date Range">
...
</Criteria>
<Criteria name="Advertisers">
<Elements>
<Element name="CheckBoxOne">
<Value>0</Value>
</Element>
<Element name="ListViewAvailable">
<Value>314</Value>
<Value>57</Value>
<Value>18886</Value>
<Value>7437</Value>
</Element>
</Elements>
</Criteria>
<Criteria name="Revenue Types">
...
</Criteria>
</Criterias>
</Report>
You can filter using predicate ([]) in combination with CROSS APPLY to shred the XML on Value elements level :
SELECT C.value('.', 'int') AS Value
FROM tbl t
CROSS APPLY t.xmlColumn.nodes('
/Report/Criterias/Criteria[#name="Advertisers"]
/Elements/Element[#name="ListViewAvailable"]
/Value
') T(C)
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 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.
I have a stored procedure that is generating an XML output for me.
The SP looks like this:
ALTER PROCEDURE [dbo].[InternFetchProf]
#positionID INT=''
AS
BEGIN
SET NOCOUNT ON;
BEGIN
SELECT A.[id],
A.[positionTitle],
(SELECT B.[id],
B.[question],
B.[order],
(SELECT D.[optionName],
D.[order]
FROM internshipProfOptions AS D
WHERE questionID = B.[id]
ORDER BY D.[order] ASC
FOR XML PATH ('optionsBlock'), TYPE, ELEMENTS, ROOT ('options'))
FROM internshipProfQuestions AS B
WHERE positionID = A.[id]
ORDER BY B.[order] ASC
FOR XML PATH ('questionBlock'), TYPE, ELEMENTS, ROOT ('questions'))
FROM internships AS a
WHERE A.[id] = #positionID
ORDER BY closeDate ASC
FOR XML PATH ('positions'), TYPE, ELEMENTS, ROOT ('root');
END
END
This gives me the XML output of
<root>
<positions>
<id>1</id>
<positionTitle>APS Team</positionTitle>
<questions>
<questionBlock>
<id>1</id>
<question>Whats your fav color</question>
<order>1</order>
<options>
<optionsBlock>
<optionName>VBA</optionName>
<order>1</order>
</optionsBlock>
<optionsBlock>
<optionName>JavaScript</optionName>
<order>2</order>
</optionsBlock>
<optionsBlock>
<optionName>HTML</optionName>
<order>3</order>
</optionsBlock>
</options>
</questionBlock>
<questionBlock>
<id>2</id>
<question>Whos your daddy?</question>
<order>2</order>
<options>
<optionsBlock>
<optionName>PHP</optionName>
<order>1</order>
</optionsBlock>
<optionsBlock>
<optionName>Perl</optionName>
<order>2</order>
</optionsBlock>
</options>
</questionBlock>
</questions>
</positions>
</root>
I have an additional SELECT statement here:
SELECT C.[groupName],
C.[order],
FROM internshipProfGroups AS C
WHERE questionID = B.[id]
FOR XML PATH ('groupBlock'), TYPE, ELEMENTS, ROOT ('groups')
Which I am trying to get to generate the XML in the root of "QuestionBlock" like so:
<questionBlock>
...
<groups>
<groupBlock>
<groupName>Testing</groupName>
</groupBlock>
<groupBlock>
<groupName>Another Test</groupName>
</groupBlock>
</groups>
...
</questionBlock>
I am struggling to get the statement in the right area to produce this. Any help with what I may be missing?
IF I understood well you need to insert the second query in the same level that "SELECT D.[OptionName]" (also I think you have some mistakes in the code):
SELECT
A.[id],
A.[positionTitle],
(SELECT B.[id],
B.[question],
B.[ord],
(SELECT D.[optionName],
D.[ord]
FROM internshipProfOptions AS D
WHERE questionID = B.[id]
ORDER BY D.[ord] ASC
FOR XML PATH ('optionsBlock'), TYPE, ELEMENTS, ROOT ('options')),
(SELECT C.[groupName],
C.[ord]
FROM internshipProfGroups AS C
WHERE questionID = B.[id]
FOR XML PATH ('groupBlock'), TYPE, ELEMENTS, ROOT ('groups'))
FROM internshipProfQuestions AS B
WHERE positionID = A.[id]
ORDER BY B.[ord] ASC
FOR XML PATH ('questionBlock'), TYPE, ELEMENTS, ROOT ('questions'))
FROM internships A
WHERE A.[id] = 3
ORDER BY closeDate ASC
FOR XML PATH ('positions'), TYPE, ELEMENTS, ROOT ('root');
WORKING SAMPLE
That gives this result:
<?xml version="1.0"?>
<root>
<positions>
<id>3</id>
<positionTitle>Trimepost</positionTitle>
<questions>
<questionBlock>
<id>2</id>
<question>What?</question>
<ord>1</ord>
<options>
<optionsBlock>
<optionName>Opt 2</optionName>
<ord>3</ord>
</optionsBlock>
</options>
<groups>
<groupBlock>
<groupName>ALPHA</groupName>
<ord>1</ord>
</groupBlock>
<groupBlock>
<groupName>BETA</groupName>
<ord>2</ord>
</groupBlock>
</groups>
</questionBlock>
</questions>
</positions>
</root>
I've created a simplified version of my problem:
DECLARE #X XML =
'<Root xmlns="TestNS" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Test>
<Id>1</Id>
<InnerCollection>
<InnerItem>
<Value>1</Value>
</InnerItem>
<InnerItem>
<Value>2</Value>
</InnerItem>
<InnerItem>
<Value>3</Value>
</InnerItem>
</InnerCollection>
</Test>
<Test>
<Id>2</Id>
<InnerCollection>
<InnerItem>
<Value>5</Value>
</InnerItem>
<InnerItem>
<Value>6</Value>
</InnerItem>
<InnerItem>
<Value>7</Value>
</InnerItem>
</InnerCollection>
</Test>
</Root>'
I'm trying to write a query that takes each <Test> element and breaks it into a row. On each row I want to select the Id and the InnerCollection as XML. I want to create this InnerCollection XML for the first row (Id:1):
<InnerCollection xmlns="Reed.Api" xmlnsi="http//www.w3.org/2001/XMLSchema-instance">
<InnerItem>
<Value>1</Value>
</InnerItem>
<InnerItem>
<Value>2</Value>
</InnerItem>
<InnerItem>
<Value>3</Value>
</InnerItem>
</InnerCollection>
I tried doing that with this query but it puts a namespace I don't want on the elements:
;WITH XMLNAMESPACES
(
DEFAULT 'TestNS'
, 'http://www.w3.org/2001/XMLSchema-instance' AS i
)
SELECT
X.value('Id[1]', 'INT') Id
-- Creates a p1 namespace that I don't want.
, X.query('InnerCollection') InnerCollection
FROM #X.nodes('//Test') AS T(X)
My Google-fu isn't very strong today, but I imagine it doesn't make it any easier that the darn function is called query. I'm open to using other methods to create that XML value other than the query method.
I could use this method:
;WITH XMLNAMESPACES
(
DEFAULT 'TestNS'
, 'http://www.w3.org/2001/XMLSchema-instance' AS i
)
SELECT
X.value('Id[1]', 'INT') Id
,CAST(
(SELECT
InnerNodes.Node.value('Value[1]', 'INT') AS 'Value'
FROM X.nodes('./InnerCollection[1]//InnerItem') AS InnerNodes(Node)
FOR XML PATH('InnerItem'), ROOT('InnerCollection')
) AS XML) AS InnerCollection
FROM #X.nodes('//Test') AS T(X)
But that involves calling nodes on it to break it out into something selectable, and then selecting it back into XML using FOR XML... when it was XML to begin with. This seems like a inefficient method of doing this, so I'm hoping someone here will have a better idea.
This is how to do the SELECT using the query method to create the XML on each row that my question was looking for:
;WITH XMLNAMESPACES
(
'http://www.w3.org/2001/XMLSchema-instance' AS i
, DEFAULT 'TestNS'
)
SELECT
Test.Row.value('Id[1]', 'INT') Id
, Test.Row.query('<InnerCollection xmlns="TestNS" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">{InnerCollection}</InnerCollection>')
FROM #X.nodes('/Root/Test') AS Test(Row)