Using a CTE to insert a parsed XML - sql

I'm developing a SQL Server 2012 stored procedure.
I want to parse this XML:
<Commissioning>
<Item ProductionId="115100" ItemId="100" ItemPackagingLevelId="1" ItemFlag="0" TimeStamp="2014-04-28T16:02:59.913" Username="" Source="PLS" />
<Item ProductionId="115101" ItemId="101" ItemPackagingLevelId="1" ItemFlag="0" TimeStamp="2014-04-28T16:03:00.113" Username="" Source="PLS" />
<Item ProductionId="115102" ItemId="102" ItemPackagingLevelId="1" ItemFlag="0" TimeStamp="2014-04-28T16:03:00.317" Username="" Source="PLS" />
<Item ProductionId="115103" ItemId="103" ItemPackagingLevelId="1" ItemFlag="0" TimeStamp="2014-04-28T16:03:00.517" Username="" Source="PLS" />
<Item ProductionId="115104" ItemId="104" ItemPackagingLevelId="1" ItemFlag="0" TimeStamp="2014-04-28T16:03:00.727" Username="" Source="PLS" />
</Commissioning>
And insert ItemId and ItemPackagingLevelId in a CTE because I want to do some calculations with that data.
How can I do it?

Like this...?
;with cte as (
select
x.n.value('./#ItemId','varchar(100)') as ItemID,
x.n.value('./#ItemPackagingLevelId','varchar(100)') as LevelID
from #x.nodes('/Commissioning/Item') x(n)
)
select * from cte

Related

SELECT FROM XML MULTIPLE ELEMENTS SQL

I have a return of XML that I wish to select using SQL. I don't have a problem selecting from the XML when I specify the index for the element. This is the XML in question.
<root httpStatusCode="200">
<messages />
<succesfulResponses>
<item position="0">
<response dln="TESTDLN" ServiceVersion="1" hubServiceVersion="1.0.0.0" ProcessingDate="2017-11-20T10:42:20.579Z" hubProcessingDate="2017-11-20T10:41:16.5415151Z" httpStatusCode="200">
<licence status="FC" validFrom="2017-03-18" validTo="2024-10-31" directiveIndicator="4">
<entitlements>
<item code="A" validFrom="2006-04-07" validTo="2057-05-20" priorTo="false" type="P">
<restrictions />
</item>
<item code="AM" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions>
<item type="122" info="null" />
</restrictions>
</item>
<item code="B" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions />
</item>
<item code="BE" validFrom="2014-11-01" validTo="2057-05-20" priorTo="false" type="P">
<restrictions />
</item>
<item code="F" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions />
</item>
<item code="G" validFrom="2014-11-01" validTo="2057-05-20" priorTo="false" type="P">
<restrictions />
</item>
<item code="H" validFrom="2014-11-01" validTo="2057-05-20" priorTo="false" type="P">
<restrictions />
</item>
<item code="K" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions />
</item>
<item code="Q" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions>
<item type="122" info="null" />
</restrictions>
</item>
</entitlements>
<endorsements />
</licence>
<messages />
</response>
</item>
</succesfulResponses>
<errorResponses />
</root>
The SQL I have written so far is as follows:
select
Rec.value('(#dln)[1]','char(50)'),
Rec.value('(licence/#status)[1]','char(2)'),
pd.value('(entitlements/item/#code)[1]','char(2)')
FROM #xmlData.nodes('//root/succesfulResponses/item/response') as x(Rec)
cross apply #xmlData.nodes('//root/succesfulResponses/item/response/licence') as i(pd)
This returns the obviously First Row code of 'A', however there can be multiple 'entitlements' and I don't ever know how many there could be 3 there could be 9.
I thought a Cross Apply would work but I can't seem to make that work either.
Any thoughts/help.
Try this - expand the XPath after the CROSS APPLY to include the entitlements/item part:
select
Rec.value('(#dln)[1]','char(50)'),
Rec.value('(licence/#status)[1]','char(2)'),
pd.value('#code', 'char(2)')
FROM
#xmlData.nodes('//root/succesfulResponses/item/response') as x(Rec)
cross apply
#xmlData.nodes('//root/succesfulResponses/item/response/licence/entitlements/item') as i(pd)
Returns:
The variant with OPENXML
DECLARE #idoc int, #doc varchar(MAX)
SET #doc='<root httpStatusCode="200">
<messages />
<succesfulResponses>
<item position="0">
<response dln="TESTDLN" ServiceVersion="1" hubServiceVersion="1.0.0.0" ProcessingDate="2017-11-20T10:42:20.579Z" hubProcessingDate="2017-11-20T10:41:16.5415151Z" httpStatusCode="200">
<licence status="FC" validFrom="2017-03-18" validTo="2024-10-31" directiveIndicator="4">
<entitlements>
<item code="A" validFrom="2006-04-07" validTo="2057-05-20" priorTo="false" type="P">
<restrictions />
</item>
<item code="AM" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions>
<item type="122" info="null" />
</restrictions>
</item>
<item code="B" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions />
</item>
<item code="BE" validFrom="2014-11-01" validTo="2057-05-20" priorTo="false" type="P">
<restrictions />
</item>
<item code="F" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions />
</item>
<item code="G" validFrom="2014-11-01" validTo="2057-05-20" priorTo="false" type="P">
<restrictions />
</item>
<item code="H" validFrom="2014-11-01" validTo="2057-05-20" priorTo="false" type="P">
<restrictions />
</item>
<item code="K" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions />
</item>
<item code="Q" validFrom="2014-10-21" validTo="2057-05-20" priorTo="false" type="F">
<restrictions>
<item type="122" info="null" />
</restrictions>
</item>
</entitlements>
<endorsements />
</licence>
<messages />
</response>
</item>
</succesfulResponses>
<errorResponses />
</root>'
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc;
SELECT *
FROM OPENXML(#idoc,'/root/succesfulResponses/item/response/licence/entitlements/item',2)
WITH (
status varchar(10) '../../#status',
code varchar(10) './#code',
validFrom date './#validFrom'
);
EXEC sp_xml_removedocument #idoc;
GO

SQL Server : Count of XML Elements By Element Name

I have successfully obtained a count of sub elements using this query.
SELECT
raa.ApplicationXML.value('count(Root/ThirdParty/*)', 'Int') as [Bureau Count]
FROM
Table1 raa
However, I want to group this count by the actual name of the sub element.
My XML looks something like this:
Record 1:
<Root>
<ThirdParty>
<BureauRed />
<BureauGreen />
<BureauBlue />
</ThirdParty>
</Root>
Record 2:
<Root>
<ThirdParty>
<BureauRed />
<BureauPurple />
<BureauBlue />
</ThirdParty>
</Root>
Record 3:
<Root>
<ThirdParty>
<BureauGreen />
<BureauRed />
<BureauPurple />
<BureauOrange />
<BureauBlue />
</ThirdParty>
</Root>
Not only do I need to pull the count of each bureau, but I need to get the name of the bureau (BureauRed, BureauGreen, BureauBlue, etc.) When I try to group by count I get the error:
XML methods are not allowed in a GROUP BY clause.
Using this query:
SELECT
raa.ApplicationXML.value('count(Root/ThirdParty/*)', 'Int') as [Bureau Count]
FROM
Table1 raa
GROUP BY
raa.ApplicationXML.value('count(Root/ThirdParty/*)', 'Int')
How do I get the count and the name of the bureau?
I'm looking for this result:
Bureau Count
============ =====
BureauRed 3
BureauGreen 2
BureauBlue 3
BureauPurple 2
BureauOrange 1
You can group after recieving data via XQuery:
DECLARE #xml1 xml =
'<Root>
<ThirdParty>
<BureauRed />
<BureauGreen />
<BureauBlue />
</ThirdParty>
</Root>';
DECLARE #xml2 xml =
'<Root>
<ThirdParty>
<BureauRed />
<BureauPurple />
<BureauBlue />
</ThirdParty>
</Root>';
DECLARE #xml3 xml =
'<Root>
<ThirdParty>
<BureauGreen />
<BureauRed />
<BureauPurple />
<BureauOrange />
<BureauBlue />
</ThirdParty>
</Root>';
DECLARE #temp TABLE (XmlData xml);
INSERT #temp VALUES (#xml1), (#xml2), (#xml3);
WITH cte AS
(
SELECT n.value('local-name(.)', 'varchar(50)') nodeName
FROM #temp t
CROSS APPLY t.XmlData.nodes('/Root/ThirdParty/*') x(n)
)
SELECT nodeName, count(*) cnt
FROM cte
GROUP BY nodeName;
Output:
nodeName cnt
-------------------------------------------------- -----------
BureauBlue 3
BureauGreen 2
BureauOrange 1
BureauPurple 2
BureauRed 3
You need to extract the elements that are children of Root/ThirdParty first and use that derived table to do the counting.
DECLARE #tv TABLE(x XML);
INSERT INTO #tv(x)VALUES(
N'<Root>
<ThirdParty>
<BureauRed />
<BureauGreen />
<BureauBlue />
</ThirdParty>
</Root>'),(
N'<Root>
<ThirdParty>
<BureauRed />
<BureauPurple />
<BureauBlue />
</ThirdParty>
</Root>'),(
N'<Root>
<ThirdParty>
<BureauGreen />
<BureauRed />
<BureauPurple />
<BureauOrange />
<BureauBlue />
</ThirdParty>
</Root>');
WITH bureaus AS (
SELECT
bureau=n.v.value('local-name(.)','NVARCHAR(256)')
FROM
#tv
CROSS APPLY x.nodes('/Root/ThirdParty/*') AS n(v)
)
SELECT
bureau,
[count]=COUNT(*)
FROM
bureaus
GROUP BY
bureau
ORDER BY
bureau;

XPATH: Filtering (rather than selecting nodes), preserving hierarachy

Given the following XML (with many hierarchy levels), I need to return the nodes after filtering all nodes with Hide"True" attribute and a certain name. Note that hide could be at any hierarchy level:
<items>
<item id="1" >
<item id="2" />
<item id="3" />
<item id="4" hide="true"/>
</item>
<item id="5" hide="true">
<item id="6" >
<item id="7">
<item id="8" hide="true"/>
<item id="9"/>
</item>
</item>
</items>
I would need to get this back:
<items>
<item id="1"">
<item id="2" />
<item id="3" />
</item>
<item id="6" >
<item id="7">
<item id="9"/>
</item>
</item>
</items>
The 'filtered' XML is then assigned to a treeview, so I need to preserve the hierarchical nature of the nodes. I have tried: "items//item[#hide='false' or not(#hide)]", but this returns a flattened (as well as duplicative) data, repeating nodes at the lower level pushed up to the top level as follows. Is there a way to use Xpath to do what I want? I understand that I can Xslt the data first, then display it, but it just seems that there is got to be an easier way. I am using c#/.net4.0 MSXML
<items>
<item id="1"">
<item id="2" />
<item id="3" />
</item>
<item id="6" >
<item id="7">
<item id="9"/>
</item>
</item>
<item id="2" />
<item id="3" />
<item id="7">
<item id="9"/>
</item>
<item id="9"/>
</items>

sql select data from XML param

whats the SQL for selecting the values from this XML chunk like done in the sample below?
<RWFCriteria reportType="OPRAProject">
<item id="88" name="" value="" type="Project" />
<item id="112" name="" value="12" type="Milestone" />
<item id="43" name="" value="11" type="Milestone" />
</RWFCriteria>
i want to select out similar to this but with the above XML data
DECLARE #Param XML
SET #Param = '<data>
<release id="1"><milestone id="1" /><milestone id="2" /></release>
<release id="3"><milestone id="1" /><milestone id="27"/></release>
</data>'
SELECT c.value('../#id', 'INT') AS ReleaseId, c.value('#id', 'INT') AS MilestoneId
FROM #Param.nodes('/data/release/milestone') AS T(c)
I want only the data in the nodes where type="Milestone"
Something like this:
DECLARE #Param XML
SET #Param = '<RWFCriteria reportType="OPRAProject">
<item id="88" name="" value="" type="Project" />
<item id="112" name="" value="12" type="Milestone" />
<item id="43" name="" value="11" type="Milestone" />
</RWFCriteria>'
SELECT
RWF.item.value('#id', 'INT') AS 'Id',
RWF.item.value('#name', 'VARCHAR(100)') AS 'Name',
RWF.item.value('#value', 'INT') AS 'Value',
RWF.item.value('#type', 'VARCHAR(100)') AS 'Type'
FROM
#Param.nodes('/RWFCriteria/item') AS RWF(item)
WHERE
RWF.item.value('#type', 'VARCHAR(100)') = 'Milestone'
Resulting output:
Id Name Value Type
112 12 Milestone
43 11 Milestone

How to replace attribute value of *all* matching elements with XQuery?

I'm trying without luck to create a modify() statement to change the value of an attribute in all elements that have that attribute value -- so far I can only get it to change the value in the first matched element. I created an example below of what I have so far, which I'm running in SQL Server 2005:
DECLARE #x XML
SELECT #x = '
<FootballApparel>
<Item Team="Phoenix Cardinals" Type="Hat" Cost="$14.99" />
<Item Team="Indianapolis Colts" Type="Hat" Cost="$14.99" />
<Item Team="Cincinnati Bengals" Type="Hat" Cost="$14.99" />
<Item Team="Phoenix Cardinals" Type="Shirt" Cost="$21.99" />
<Item Team="Indianapolis Colts" Type="Shirt" Cost="$21.99" />
<Item Team="Cincinnati Bengals" Type="Shirt" Cost="$21.99" />
</FootballApparel>
';
SET #x.modify('
replace value of
(/FootballApparel/Item[#Team="Phoenix Cardinals"]/#Team)[1]
with "Arizona Cardinals"
');
SELECT #x;
Running this gives the results below -- only the first instance of Phoenix Cardinals has been changed.
<FootballApparel>
<Item Team="Arizona Cardinals" Type="Hat" Cost="$14.99" />
<Item Team="Indianapolis Colts" Type="Hat" Cost="$14.99" />
<Item Team="Cincinnati Bengals" Type="Hat" Cost="$14.99" />
<Item Team="Phoenix Cardinals" Type="Shirt" Cost="$21.99" />
<Item Team="Indianapolis Colts" Type="Shirt" Cost="$21.99" />
<Item Team="Cincinnati Bengals" Type="Shirt" Cost="$21.99" />
</FootballApparel>
Can you please help me with the correct modify() statement to replace all instances?
Thanks!
Kevin
You're very close - what you need to do is loop (and there's no other way I know of to do it in this case) and repeatedly replace the values:
WHILE #x.exist('(/FootballApparel/Item[#Team=sql:variable("#oldTeamName")])[1]') = 1
SET #x.modify('
replace value of (
/FootballApparel/Item[#Team=sql:variable("#oldTeamName")]/#Team
)[1]
with sql:variable("#newTeamName")
');
That should do the trick.
Marc