SQL Server : Count of XML Elements By Element Name - sql

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;

Related

Unable to extract details from an XML column of MSSQL

I am new to extracting data from XML column using SQL, I did try with other simple format but this one is a bit tricky.
XML column name is "Data" and below is the value.
<SpecificationDataDto
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/Dummy.Sample.API.TransferObjects.Dto">
<Narratives>
<AnswerDto>
<AddressData />
<AddressResponseType i:nil="true" />
<Answers
xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:string>Loose Print</d4p1:string>
</Answers>
<ComponentOrderNumber>3</ComponentOrderNumber>
<Index>0</Index>
<IsQuantity>false</IsQuantity>
<MasterQuestionId i:nil="true" />
<MasterQuestionTitle i:nil="true" />
<MasterQuestionTitleResourceKey i:nil="true" />
<ParentQuestionGroupId i:nil="true" />
<ParentQuestionGroupIndex i:nil="true" />
<ParentQuestionGroupTitle i:nil="true" />
<PermutationId>65922bf8-6468-40f2-967c-27547412ba2b</PermutationId>
<PermutationOrder>0</PermutationOrder>
<ProductSpecificationLabel />
<ProductSpecificationLabelResourceKey>65A4E3D8-9FF7-48D9-B3F8-0E2D4D23A1B8</ProductSpecificationLabelResourceKey>
<QuestionGroupId i:nil="true" />
<QuestionGroupIndex i:nil="true" />
<QuestionGroupOrderNumber i:nil="true" />
<QuestionGroupTitle i:nil="true" />
<QuestionId>e575d4ac-5dfb-4170-c5c0-08da85c9a9ed</QuestionId>
<QuestionOrderNumber>1</QuestionOrderNumber>
<QuestionTitle>Product category</QuestionTitle>
<QuestionTitleResourceKey>81B4FFBC-4503-4E48-83C0-17FBB227384B</QuestionTitleResourceKey>
<ResponseIds
xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d4p1:guid>c4ed5170-6634-4aad-c054-08da7f653c36</d4p1:guid>
</ResponseIds>
<UpdateDate i:nil="true" />
</AnswerDto>
</Narratives>
</SpecificationDataDto>
I tried below but no luck
WITH XMLNAMESPACES ('http://schemas.microsoft.com/2003/10/Serialization/Arrays' AS d4p1)
, CTE as (
SELECT Order
,t.c.value('(*/QuestionTitle)[1]', 'Varchar(250)') AS Question
,t.c.value('(*/d4p1:string)[1]', 'Varchar(250)') AS Answer
FROM SchemaName.DatabaseName.TableName
CROSS APPLY Data.nodes('*/SpecificationDataDto/Narratives/AnswerDto') t(c)
WHERE Order = '737B4994'
)
select * from CTE
I am expecting the below values to come out for this order in 2 columns,
QuestionTitle = Product category
d4p1:string = Loose Print

Get XML from different Tables

Good day to you!
I have a question. Can't get how combine 2 tables in 1 result XML.
Here is the sample
DECLARE #t1 table (ID int identity(1,1), SomeField varchar(50))
DECLARE #t2 table (ID int identity(1,1), SomeField varchar(50), AnotherField varchar(50))
INSERT INTO #t1 (SomeField) VALUES ('rec1'),('rec2'),('rec3'),('rec4')
INSERT INTO #t2 (SomeField,AnotherField) VALUES ('s106','here'),('s12','just'),('s13','sample')
SELECT * FROM #t1 AS FirstTable
SELECT * FROM #t2 AS AnotherTable
Wanted result:
<Root>
<FirstTable ID="1" SomeField="rec1" />
<FirstTable ID="2" SomeField="rec2" />
<FirstTable ID="3" SomeField="rec3" />
<FirstTable ID="4" SomeField="rec4" />
<AnotherTable ID="1" SomeField="s106" AnotherField="here" />
<AnotherTable ID="2" SomeField="s12" AnotherField="just" />
<AnotherTable ID="3" SomeField="s13" AnotherField="sample" />
</Root>
dbfiddle here
New remark (edited)
answered by John Cappelletti, but need put all this inside third table.
Here is new code:
DECLARE #t1 table (ID int identity(1,1), tID int, SomeField varchar(50))
DECLARE #t2 table (ID int identity(1,1), tID int, SomeField varchar(50), AnotherField varchar(50))
DECLARE #t3 table (ID int identity(1,1), field1 varchar(50), field2 varchar(50))
INSERT INTO #t1 (tID,SomeField) VALUES (1,'rec1'),(1,'rec2'),(1,'rec3'),(1,'rec4')
INSERT INTO #t2 (tID,SomeField,AnotherField) VALUES (1,'s106','here'),(1,'s12','just'),(1,'s13','sample')
INSERT INTO #t3 (field1,field2) VALUES ('field1 Value','field2 Value')
Wanted result (finally):
<ThirdTable ID="1" field1="field1 Value" field2="field2 Value">
<FirstTable ID="1" tID="1" SomeField="rec1" />
<FirstTable ID="2" tID="1" SomeField="rec2" />
<FirstTable ID="3" tID="1" SomeField="rec3" />
<FirstTable ID="4" tID="1" SomeField="rec4" />
<AnotherTable ID="1" tID="1" SomeField="s106" AnotherField="here" />
<AnotherTable ID="2" tID="1" SomeField="s12" AnotherField="just" />
<AnotherTable ID="3" tID="1" SomeField="s13" AnotherField="sample" />
</ThirdTable>
Select [root] = cast((Select * From #t1 for xml raw('FirstTable'))
+(Select * From #t2 for xml raw('AnotherTable'))
as xml)
For XML Path(''),Type
Returns
<root>
<FirstTable ID="1" SomeField="rec1" />
<FirstTable ID="2" SomeField="rec2" />
<FirstTable ID="3" SomeField="rec3" />
<FirstTable ID="4" SomeField="rec4" />
<AnotherTable ID="1" SomeField="s106" AnotherField="here" />
<AnotherTable ID="2" SomeField="s12" AnotherField="just" />
<AnotherTable ID="3" SomeField="s13" AnotherField="sample" />
</root>
Added for the Extended Question
Select *
,(select cast(
isnull((Select * From #t1 for xml raw('FirstTable')),'')
+isnull((Select * From #t2 for xml raw('AnotherTable')),'')
as xml)
)
From #t3 for xml raw('ThirdTable')
Returns
<ThirdTable ID="1" field1="field1 Value" field2="field2 Value">
<FirstTable ID="1" tID="1" SomeField="rec1" />
<FirstTable ID="2" tID="1" SomeField="rec2" />
<FirstTable ID="3" tID="1" SomeField="rec3" />
<FirstTable ID="4" tID="1" SomeField="rec4" />
<AnotherTable ID="1" tID="1" SomeField="s106" AnotherField="here" />
<AnotherTable ID="2" tID="1" SomeField="s12" AnotherField="just" />
<AnotherTable ID="3" tID="1" SomeField="s13" AnotherField="sample" />
</ThirdTable>

Using a CTE to insert a parsed XML

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

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

T-SQL Dynamic xquery

I am trying to figure out how I can load my table variable with data from XML using dynamic xquery? I am getting a result set of nodes from the query and defining the value type of those nodes. It seems that it is the value definition of the nodes that it is blowing up on.
Here is an example of the script that works, but is not dynamic.
Script:
DECLARE #XML XML = '<root>
<data>
<list id="organization" label="Organization">
<options>
<item value="1" label="Organization1" selected="false" />
<item value="2" label="Organization2" selected="false" />
<item value="3" label="Organization3" selected="false" />
<item value="4" label="Organization4" selected="true" />
<item value="5" label="Organization5" selected="true" />
</options>
</list>
</data>
</root>';
DECLARE #Orgs TABLE (ID INT);
Insert Into #Orgs(ID) Select OrgNameIdNodes.ID.value('#value','int') from #xml.nodes('//*[#id="organization"]//item[#selected="true"]') as OrgNameIdNodes(ID);
Select *
from #orgs
What I would like to be able to do is pass in parameters for both value and the #xml.nodes sections so I would have something like:
Insert Into #Orgs(ID) Select OrgNameIdNodes.ID.value(#Value) from #xml.nodes(#Nodes) as OrgNameIdNodes(ID);
Is this possible?
How about using sp_executesql with dynamic sql. Something like:
DECLARE #XML XML = '<root>
<data>
<list id="organization" label="Organization">
<options>
<item value="1" label="Organization1" selected="false" />
<item value="2" label="Organization2" selected="false" />
<item value="3" label="Organization3" selected="false" />
<item value="4" label="Organization4" selected="true" />
<item value="5" label="Organization5" selected="true" />
</options>
</list>
</data>
</root>';
declare #orgs table(ID int);
declare #nodes nvarchar(4000),
#value nvarchar(4000),
#query nvarchar(4000)
select #value = '''#value'',''int'''
select #nodes = '//*[#id="organization"]//item[#selected="true"]'
select #query = 'Select OrgNameIdNodes.ID.value( ' + #value + ') ' +
'from #xml.nodes(''' + #nodes + ''') as OrgNameIdNodes(ID)'
insert into #Orgs(ID) EXEC sp_executesql #query, N'#xml xml', #xml = #xml
Select *
from #orgs