How do I partially share param values across union selects using different constraints? - sql

I have a view
create or replace view v_collected as
select car.id_car
,car.state_car
,[..]
from cars car
,garages garage
,[..]
where [..]
Which is at least unique for car.id
Now I want to union 3 different queries into a second view, which takes user set parameters:
select 0 as score
,p0.*
from v_collected p0
where 1 = 1
-- User IO Binding
and p0.car = 'Audi'
and p0.garage = 'P01'
and p0.state_car = 'Ok'
union
select 1 as score
,p1.*
from v_collected p1
where 1 = 1
-- Should access the identical binding
and p1.car <> p0.car
and p1.state_car = p0.state_car
union
select 2 as score
,p2.*
from v_collected p2
where 1 = 1
-- Should access the identical binding
and p2.state_car = p0.state_car
The above does not work as the 2nd query has no access to the the 1st one. As the constraints change, I believe I cannot use a CTE. What are my options?

Since the requirements are vague, I can't say for sure that the following solution is complete, however, I would look into breaking the p0, p1, p2 into with clause sub queries that way you can use p0 in p1 and p2. For example:
with cars as
(
select 2 id_car, 'Ford' car, 'Ok' state_car from dual union
select 1 id_car, 'Audi' car, 'Ok' state_car from dual
)
, garages as
(
select 2 id_car, 'P02' garage from dual union
select 1 id_car, 'P01' garage from dual
)
, v_collected as
(
select car.id_car
,car.car
,car.state_car
,garage.garage
from cars car
,garages garage
where 1=1
and car.id_car = garage.id_car
)
-- select * from v_collected;
, p0_subquery as
(
select 0 as score
,p0.*
from v_collected p0
where 1 = 1
-- User IO Binding
and p0.car = 'Audi'
and p0.garage = 'P01'
and p0.state_car = 'Ok'
)
--select * from p0_subquery;
, p1_subquery as
(
select 1 as score
,p1.*
from v_collected p1
, p0_subquery p0
where 1 = 1
-- Should access the identical binding
and p1.car <> p0.car
and p1.state_car = p0.state_car
)
, p2_subquery as
(
select 2 as score
,p2.*
from v_collected p2
, p0_subquery p0
where 1 = 1
-- Should access the identical binding
and p2.state_car = p0.state_car
)
select * from p0_subquery
union
select * from p1_subquery
union
select * from p2_subquery
;

Related

Oracle Query : One to Many Relationship

In my application there are one-to-many relationship as explained below.
Table one : Application
app_id
app_name
1
ABC
2
XYZ
Table two : Application_attribute [One application can have multiple attribute and variable list of attribute]
App_attr_id
app_id
attr_name
attr_value
1
1
attr1
white
2
1
attr2
12
3
1
attr3
45
4
2
attr1
red
5
2
attr2
12
6
2
attr4
45
7
2
attr7
62
Each application can have variable list of attributes.
Query Requirement
I want to fetch list of application based on multiple attribute.
Example get list of application whose attributes are attr1=white,attr2=12,attr3=45
In above case, problem can be solved by joining application table with application_attribute table 3 times but attribute will vary per application so it will not be generic solution.
Query Solution for my requirement
SELECT a.*
FROM application a,
application_attribute at1,
application_attribute at2,
application_attribute at3
WHERE a.app_id = at1.app_id
AND a.app_id = at2.app_id
AND a.app_id = at3.app_id
AND at1.attr_name = 'attr1'
AND at1.attr_value = 'white'
AND at2.attr_name = 'attr2'
AND at2.attr_value = '12'
AND at3.attr_name = 'attr3'
AND at3.attr_value = '45'
Expected Result
app_id
app_name
1
ABC
One option is two create dynamic query. Is it possible to write generic query which can be used to search n number of attribute like 3,4,5..n ?
You want to select applications where exist certain attributes. So, select from the application table and have a where clause checkink the existence of the attributes with EXISTS or IN:
select *
from aplication
where app_id in (select app_id from application_attribute where attr_name = 'attr1' and attr_value = 'white')
and app_id in (select app_id from application_attribute where attr_name = 'attr2' and attr_value = '12')
and app_id in (select app_id from application_attribute where attr_name = 'attr3' and attr_value = '45')
order by app_id;
As to generic: Simply build the query with a programming language and a loop over the desired attributes. In Oracle you can use PL/SQL for this.
You can use EXISTS and a HAVING clause in the correlated sub-query:
SELECT *
FROM application a
WHERE EXISTS (
SELECT 1
FROM application_attribute aa
WHERE a.app_id = aa.app_id
AND (aa.attr_name, aa.attr_value)
IN (('attr1', 'white'), ('attr2', '12'), ('attr3', '45'))
HAVING COUNT(/*DISTINCT*/ aa.attr_name) = 3
)
Note: If there can be duplicate attribute values then you can COUNT(DISTINCT ...) rather than just COUNT(...).
Which, for the sample data:
CREATE TABLE application (app_id, app_name) AS
SELECT 1, 'ABC' FROM DUAL UNION ALL
SELECT 2, 'XYZ' FROM DUAL;
CREATE TABLE Application_attribute (App_attr_id, app_id, attr_name, attr_value) AS
SELECT 1, 1, 'attr1', 'white' FROM DUAL UNION ALL
SELECT 2, 1, 'attr2', '12' FROM DUAL UNION ALL
SELECT 3, 1, 'attr3', '45' FROM DUAL UNION ALL
SELECT 4, 2, 'attr1', 'red' FROM DUAL UNION ALL
SELECT 5, 2, 'attr2', '12' FROM DUAL UNION ALL
SELECT 6, 2, 'attr4', '45' FROM DUAL UNION ALL
SELECT 7, 2, 'attr7', '62' FROM DUAL;
Outputs:
APP_ID
APP_NAME
1
ABC
db<>fiddle here
You can join tables only once by conditionally matching the values of attr_name and attr_value columns while filtering out the result only for the returning records with exactly triple attribute names such as
SELECT a.app_id, a.app_name
FROM application a
JOIN application_attribute aa
ON aa.app_id = a.app_id
WHERE DECODE( aa.attr_name, 'attr1', aa.attr_value ) = 'white' -- you can make these literals parametric by prefixing with : or & such as &p_attr1 and enter 'white' whenever prompted
OR DECODE( aa.attr_name, 'attr2', aa.attr_value ) = '12'
OR DECODE( aa.attr_name, 'attr3', aa.attr_value ) = '45'
GROUP BY a.app_id, a.app_name
HAVING COUNT(DISTINCT aa.attr_name)=3
Demo
If we re-write your query using joins there will be the 3 variables that you need to change in the WHERE. This will make it much easier to modify them.
Select a.*
from application a
left join application-attribute at1 on a.app_id = at1.app_id and at1.attr_name = 'attr1'
left join application_attribute at2 on a.app_id = at2.app_id and at2.attr_name = 'attr2'
left join application_attribute at3 on a.app_id = at3.app_id and at3.attr_name = 'attr3'
left join application_attribute at4 on a.app_id = at4.app_id and at4.attr_name = 'attr4'
where at1.attr_value = 'white'
and at2.attr_value = '12'
and at3.attr_value = '45'
/* and at4.attr_value = not needed */
;

How to display null values in IN operator for SQL with two conditions in where

I have this query
select *
from dbo.EventLogs
where EntityID = 60181615
and EventTypeID in (1, 2, 3, 4, 5)
and NewValue = 'Received'
If 2 and 4 does not exist with NewValue 'Received' it shows this
current results
What I want
Ideally you should maintain somewhere a table containing all possible EventTypeID values. Sans that, we can use a CTE in place along with a left join:
WITH EventTypes AS (
SELECT 1 AS ID UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5
)
SELECT et.ID AS EventTypeId, el.*
FROM EventTypes et
LEFT JOIN dbo.EventLogs el
ON el.EntityID = 60181615 AND
el.NewValue = 'Received'
WHERE
et.ID IN (1,2,3,4,5);

Excluding records from table based on rules from another table

I'm using Oracle SQL and I have a product table with diffrent attributes and sales volume for each product and another table with certain exclusion rules for different level of aggregation. Let's look at the example:
Here is our main table with sales data on which we want to perform some calculations:
And the other table contains diffrent rules which are supposed to exclude certain rows from table above:
When there is an "x", this column shouldn't be considered so our rules are:
1. exclude all rows with ATTR_3 = 'no'
2. exlcude all rows with ATTR_1 = 'Europe' and ATTR_2 = 'snacks' and ATTR_3 = 'no'
3. exlcude all rows with ATTR_1 = 'Africa'
And based on that our final output should be like that:
How this could be achived in SQL? I was thinking about join but I have no idea how to handle different levels of aggregation for exclusions.
I think your expected output is wrong. None of the rules excludes the 2nd row (Europe - snacks - yes).
SQL> with
2 -- sample data
3 test (product_id, attr_1, attr_2, attr_3) as
4 (select 81928 , 'Europe', 'beverages', 'yes' from dual union all
5 select 16534 , 'Europe', 'snacks' , 'yes' from dual union all
6 select 56468 , 'USA' , 'snacks' , 'no' from dual union all
7 select 129921, 'Africa', 'drinks' , 'yes' from dual union all
8 select 123021, 'Africa', 'snacks' , 'yes' from dual union all
9 select 165132, 'USA' , 'drinks' , 'yes' from dual
10 ),
11 rules (attr_1, attr_2, attr_3) as
12 (select 'x' , 'x' , 'no' from dual union all
13 select 'Europe', 'snacks', 'no' from dual union all
14 select 'Africa', 'x' , 'x' from dual
15 )
16 -- query you need
17 select t.*
18 from test t
19 where (t.attr_1, t.attr_2, t.attr_3) not in
20 (select
21 decode(r.attr_1, 'x', t.attr_1, r.attr_1),
22 decode(r.attr_2, 'x', t.attr_2, r.attr_2),
23 decode(r.attr_3, 'x', t.attr_3, r.attr_3)
24 from rules r
25 );
PRODUCT_ID ATTR_1 ATTR_2 ATT
---------- ------ --------- ---
81928 Europe beverages yes
16534 Europe snacks yes
165132 USA drinks yes
SQL>
You can use the join using CASE .. WHEN statement as follows:
SELECT P.*
FROM PRODUCT P
JOIN RULESS R ON
(R.ATTR_1 ='X' OR P.ATTR_1 <> R.ATTR_1)
AND (R.ATTR_2 ='X' OR P.ATTR_2 <> R.ATTR_2)
AND (R.ATTR_3 ='X' OR P.ATTR_3 <> R.ATTR_3)
You can use NOT EXISTS
SELECT *
FROM sales s
WHERE NOT EXISTS (
SELECT 0
FROM attributes a
WHERE ( ( a.attr_1 = s.attr_1 AND a.attr_1 IS NOT NULL )
OR a.attr_1 IS NULL )
AND ( ( a.attr_2 = s.attr_2 AND a.attr_2 IS NOT NULL )
OR a.attr_2 IS NULL )
AND ( ( a.attr_3 = s.attr_3 AND a.attr_3 IS NOT NULL )
OR a.attr_3 IS NULL )
)
where I considered the x values within the attributes table as NULL. If you really have x characters, then you can use :
SELECT *
FROM sales s
WHERE NOT EXISTS (
SELECT 0
FROM attributes a
WHERE ( ( NVL(a.attr_1,'x') = s.attr_1 AND NVL(a.attr_1,'x')!='x' )
OR NVL(a.attr_1,'x')='x' )
AND ( ( NVL(a.attr_2,'x') = s.attr_2 AND NVL(a.attr_2,'x')!='x' )
OR NVL(a.attr_2,'x')='x' )
AND ( ( NVL(a.attr_3,'x') = s.attr_3 AND NVL(a.attr_3,'x')!='x' )
OR NVL(a.attr_3,'x')='x' )
)
instead.
Demo
I would do this with three different not exists:
select p.*
from product p
where not exists (select 1
from rules r
where r.attr_1 = p.attr_1 and r.attr_1 <> 'x'
) and
not exists (select 1
from rules r
where r.attr_2 = p.attr_2 and r.attr_2 <> 'x'
) and
not exists (select 1
from rules r
where r.attr_3 = p.attr_3 and r.attr_3 <> 'x'
) ;
In particular, this can take advantage of indexes on (attr_1), (attri_2) and (attr_3) -- something that is quite handy if you have a moderate number of rules.

SQL Server : split row into multiple rows based on a column value

I have a question regarding splitting rows based on column value
My example data set is :
id ExpenseType Price
------------------------
1 Car 100
2 Hotel 50
I want to split rows those have some Expense Types such as Car into two rows . Others should remain as one row.
First row Price *70
Second Row Price *30
Returned dataset should be
id ExpenseType Price
-----------------------
1 Car 70
1 Car 30
2 Hotel 50
Thanks for your answers in advance
If you want to split more expense types than car you could use:
WITH r AS (
SELECT 'Car' AS ExpenseType, 0.7 AS Ratio
UNION SELECT 'Car' AS ExpenseType, 0.3 AS Ratio
-- add more ExpenseTypes/Ratios here
)
SELECT
t.id,
t.ExpenseType,
t.Price * ISNULL(r.Ratio, 1.0) AS Price
FROM
your_table t
LEFT OUTER JOIN r ON t.ExpenseType = r.ExpenseType
A simple way uses union all:
select id, expensetype, price
from t
where expensetype <> 'Car'
union all
select id, expensetype, price * 0.7
from t
where expensetype = 'Car'
union all
select id, expensetype, price * 0.3
from t
where expensetype = 'Car';
This is not the most efficient method. For that, a cross apply with filtering logic is better:
select t.id, v.*
from t cross apply
(values (NULL, price), ('Car', price * 0.3), ('Car', price * 0.7)
) v(expensetype, price)
where v.expensetype = t.expense_type or
v.expensetype <> 'Car' and t.expense_type is null;
A less simple way is to use an OUTER APPLY
CREATE TABLE YourSampleData
(
Id INT IDENTITY(1,1) PRIMARY KEY,
ExpenseType VARCHAR(30) NOT NULL,
Price INT NOT NULL DEFAULT 0
);
INSERT INTO YourSampleData
(ExpenseType, Price) VALUES
('Car', 100)
,('Hotel', 50)
,('Gold', 1)
;
SELECT Id, ExpenseType
, COALESCE(a.Price, t.Price) AS Price
FROM YourSampleData t
OUTER APPLY
(
SELECT Price * Perc AS Price
FROM (VALUES
('Car',0.3E0), ('Car',0.7E0)
,('Gold',1.618E0)
) AS v(ExpType, Perc)
WHERE t.ExpenseType = v.ExpType
) a
GO
Id | ExpenseType | Price
-: | :---------- | ----:
1 | Car | 30
1 | Car | 70
2 | Hotel | 50
3 | Gold | 1.618
db<>fiddle here
I ran into a similar need, here is my solution.
Problem statement:
My organization is switching from an in-house build system to a third-party system. Numerical values in the original system surpassed the value size that the destination system could handle. The third-party system will not allow us to increase the field size, as a result we need to split the data up into values that do not surpass the field size limit.
Details:
Destination system can only support values under 1 billion (can include a negative sign)
Example:
DROP TABLE IF EXISTS #MyDemoData /* Generate some fake data for the demo */
SELECT item_no = 1, item_description = 'zero asset', amount = 0 INTO #MyDemoData
UNION SELECT item_no = 2, item_description = 'small asset', amount = 5100000
UNION SELECT item_no = 3, item_description = 'mid asset', amount = 510000000
UNION SELECT item_no = 4, item_description = 'large asset', amount = 5100000000
UNION SELECT item_no = 5, item_description = 'large debt', amount = -2999999999.99
SELECT * FROM #MyDemoData
DECLARE #limit_size INT = 1000000000
DROP TABLE IF EXISTS #groupings;
WITH
max_groups AS
(
SELECT max_groups=100
)
,groups AS
(
SELECT 1 AS [group]
UNION ALL
SELECT [group]+1
FROM groups
JOIN max_groups ON 1=1
WHERE [group]+1<=max_groups
)
,group_rows AS
(
SELECT 0 AS [row]
UNION ALL
SELECT [row]+1
FROM group_rows
JOIN max_groups ON 1=1
WHERE [row]+1<=max_groups
)
,groupings AS
(
SELECT [group],[row]
FROM group_rows
CROSS JOIN groups
WHERE [row] <= [group]
)
SELECT * INTO #groupings FROM groupings;
WITH /* Split out items that are under the limit and over the limit */
t1 AS /* Identify rows that are over the limit and by how many multiples over it is */
(
SELECT
item_no
, item_description
, amount
, over_limit = FLOOR(ABS(amount/#limit_size))
FROM #MyDemoData
)
SELECT /* select the items that are under the limit and do not need manipulated */
item_no
, item_description
, amount = CAST(amount AS DECIMAL(16,2))
FROM t1
WHERE ABS([amount]) < #limit_size
UNION ALL /* select the items that are over the limit, join on the groupings cte and calculate the split amounts */
SELECT
item_no
, item_description
, [Amount] = CAST(
CASE
WHEN row != 0 THEN (#limit_size-1) * ([amount]/ABS([amount]))
ELSE (ABS([amount]) - (t1.over_limit * #limit_size) + t1.over_limit) * ([amount]/ABS([amount]))
END AS DECIMAL(16,2))
FROM t1
JOIN #groupings bg ON t1.over_limit = bg.[group]
WHERE ABS([amount]) >= #limit_size
ORDER BY item_no

SQL hierarchy count totals report

I'm creating a report with SQL server 2012 and Report Builder which must show the total number of Risks at a high, medium and low level for each Parent Element.
Each Element contains a number of Risks which are rated at a certain level. I need the total for the Parent Elements. The total will include the number of all the Child Elements and also the number the Element itself may have.
I am using CTEs in my query- the code I have attached isn't working (there are no errors - it's just displaying the incorrect results) and I'm not sure that my logic is correct??
Hopefully someone can help. Thanks in advance.
My table structure is:
ElementTable
ElementTableId(PK) ElementName ElementParentId
RiskTable
RiskId(PK) RiskName RiskRating ElementId(FK)
My query:
WITH cte_Hierarchy(ElementId, ElementName, Generation, ParentElementId)
AS (SELECT ElementId,
NAME,
0,
ParentElementId
FROM Extract.Element AS FirtGeneration
WHERE ParentElementId IS NULL
UNION ALL
SELECT NextGeneration.ElementId,
NextGeneration.NAME,
Parent.Generation + 1,
Parent.ElementId
FROM Extract.Element AS NextGeneration
INNER JOIN cte_Hierarchy AS Parent
ON NextGeneration.ParentElementId = Parent.ElementId),
CTE_HighRisk
AS (SELECT r.ElementId,
Count(r.RiskId) AS HighRisk
FROM Extract.Risk r
WHERE r.RiskRating = 'High'
GROUP BY r.ElementId),
CTE_LowRisk
AS (SELECT r.ElementId,
Count(r.RiskId) AS LowRisk
FROM Extract.Risk r
WHERE r.RiskRating = 'Low'
GROUP BY r.ElementId),
CTE_MedRisk
AS (SELECT r.ElementId,
Count(r.RiskId) AS MedRisk
FROM Extract.Risk r
WHERE r.RiskRating = 'Medium'
GROUP BY r.ElementId)
SELECT rd.ElementId,
rd.ElementName,
rd.ParentElementId,
Generation,
HighRisk,
MedRisk,
LowRisk
FROM cte_Hierarchy rd
LEFT OUTER JOIN CTE_HighRisk h
ON rd.ElementId = h.ElementId
LEFT OUTER JOIN CTE_MedRisk m
ON rd.ElementId = m.ElementId
LEFT OUTER JOIN CTE_LowRisk l
ON rd.ElementId = l.ElementId
WHERE Generation = 1
Edit:
Sample Data
ElementTableId(PK) -- ElementName -- ElementParentId
1 ------------------- Main --------------0
2 --------------------Element1-----------1
3 --------------------Element2 ----------1
4 --------------------SubElement1 -------2
RiskId(PK) RiskName RiskRating ElementId(FK)
a -------- Financial -- High ----- 2
b -------- HR --------- High ----- 3
c -------- Marketing -- Low ------- 2
d -------- Safety -----Medium ----- 4
Sample Output:
Element Name High Medium Low
Main ---------- 2 ---- 1 -------1
Here is your sample tables
SELECT * INTO #TABLE1
FROM
(
SELECT 1 ElementTableId, 'Main' ElementName ,0 ElementParentId
UNION ALL
SELECT 2,'Element1',1
UNION ALL
SELECT 3, 'Element2',1
UNION ALL
SELECT 4, 'SubElement1',2
)TAB
SELECT * INTO #TABLE2
FROM
(
SELECT 'a' RiskId, 'Fincancial' RiskName,'High' RiskRating ,2 ElementId
UNION ALL
SELECT 'b','HR','High',3
UNION ALL
SELECT 'c', 'Marketing','Low',2
UNION ALL
SELECT 'd', 'Safety','Medium',4
)TAB
We are finding the children of a parent, its count of High,Medium and Low and use cross join to show parent with all the combinations of its children's High,Medium and Low
UPDATE
The below variable can be used to access the records dynamically.
DECLARE #ElementTableId INT;
--SET #ElementTableId = 1
And use the above variable inside the query
;WITH CTE1 AS
(
SELECT *,0 [LEVEL] FROM #TABLE1 WHERE ElementTableId = #ElementTableId
UNION ALL
SELECT E.*,e2.[LEVEL]+1 FROM #TABLE1 e
INNER JOIN CTE1 e2 on e.ElementParentId = e2.ElementTableId
AND E.ElementTableId<>#ElementTableId
)
,CTE2 AS
(
SELECT E1.*,E2.*,COUNT(RiskRating) OVER(PARTITION BY RiskRating) CNT
from CTE1 E1
LEFT JOIN #TABLE2 E2 ON E1.ElementTableId=E2.ElementId
)
,CTE3 AS
(
SELECT DISTINCT T1.ElementName,C2.RiskRating,C2.CNT
FROM #TABLE1 T1
CROSS JOIN CTE2 C2
WHERE T1.ElementTableId = #ElementTableId
)
SELECT *
FROM CTE3
PIVOT(MIN(CNT)
FOR RiskRating IN ([High], [Medium],[Low])) AS PVTTable
SQL FIDDLE
RESULT
UPDATE 2
I am updating as per your new requirement
Here is sample table in which I have added extra data to test
SELECT * INTO #ElementTable
FROM
(
SELECT 1 ElementTableId, 'Main' ElementName ,0 ElementParentId
UNION ALL
SELECT 2,'Element1',1
UNION ALL
SELECT 3, 'Element2',1
UNION ALL
SELECT 4, 'SubElement1',2
UNION ALL
SELECT 5, 'Main 2',0
UNION ALL
SELECT 6, 'Element21',5
UNION ALL
SELECT 7, 'SubElement21',6
UNION ALL
SELECT 8, 'SubElement22',7
UNION ALL
SELECT 9, 'SubElement23',7
)TAB
SELECT * INTO #RiskTable
FROM
(
SELECT 'a' RiskId, 'Fincancial' RiskName,'High' RiskRating ,2 ElementId
UNION ALL
SELECT 'b','HR','High',3
UNION ALL
SELECT 'c', 'Marketing','Low',2
UNION ALL
SELECT 'd', 'Safety','Medium',4
UNION ALL
SELECT 'e' , 'Fincancial' ,'High' ,5
UNION ALL
SELECT 'f','HR','High',6
UNION ALL
SELECT 'g','HR','High',6
UNION ALL
SELECT 'h', 'Marketing','Low',7
UNION ALL
SELECT 'i', 'Safety','Medium',8
UNION ALL
SELECT 'j', 'Safety','High',8
)TAB
I have written the logic in query
;WITH CTE1 AS
(
-- Here you will find the level of every elements in the table
SELECT *,0 [LEVEL]
FROM #ElementTable WHERE ElementParentId = 0
UNION ALL
SELECT ET.*,CTE1.[LEVEL]+1
FROM #ElementTable ET
INNER JOIN CTE1 on ET.ElementParentId = CTE1.ElementTableId
)
,CTE2 AS
(
-- Filters the level and find the major parant of each child
-- ie, 100->150->200, here the main parent of 200 is 100
SELECT *,CTE1.ElementTableId MajorParentID,CTE1.ElementName MajorParentName
FROM CTE1 WHERE [LEVEL]=1
UNION ALL
SELECT CTE1.*,CTE2.MajorParentID,CTE2.MajorParentName
FROM CTE1
INNER JOIN CTE2 on CTE1.ElementParentId = CTE2.ElementTableId
)
,CTE3 AS
(
-- Since each child have columns for main parent id and name,
-- you will get the count of each element corresponding to the level you have selected directly
SELECT DISTINCT CTE2.MajorParentName,RT.RiskRating ,
COUNT(RiskRating) OVER(PARTITION BY MajorParentID,RiskRating) CNT
FROM CTE2
JOIN #RiskTable RT ON CTE2.ElementTableId=RT.ElementId
)
SELECT MajorParentName, ISNULL([High],0)[High], ISNULL([Medium],0)[Medium],ISNULL([Low],0)[Low]
FROM CTE3
PIVOT(MIN(CNT)
FOR RiskRating IN ([High], [Medium],[Low])) AS PVTTable
SQL FIDDLE