SQL query that summarizes each group - sql

I need help with this. I need to summarize each group of tests. The result should look like this:
name_of_the_group | all_test_cases | passed_test_cases | total_value
numerical stability 4 4 80
memory usage 3 2 20
corner cases 0 0 0
performance 2 0 0
columns:
name_of_the_group (all groups need to be displayed even if there are no corresponding tests)
all_test_cases (number of tests in the group)
passed_test_cases (number of test cases with the status OK)
total_value (total value of passed tests in this group)
Here is the fiddle example: http://sqlfiddle.com/#!17/e96109

Below SQL query provide a solution to all test cases
When there is a name entry in table test_groups but missing in test_cases.
Order by test_value desc first and then by name lexicographically increasing
order.
SELECT CASE
WHEN g.name = c.group_name THEN c.group_name
ELSE g.name
END AS name,
count(c.group_name) AS all_test_cases,
count(CASE
WHEN c.status = 'OK' THEN 1
END) AS all_test_cases,
sum(CASE
WHEN c.status = 'OK' THEN g.test_value
ELSE 0
END) AS total_value
FROM test_groups g
LEFT JOIN test_cases c ON g.name = c.group_name
GROUP BY CASE
WHEN g.name = c.group_name THEN c.group_name
ELSE g.name
END
ORDER BY total_value DESC,
CASE
WHEN g.name = c.group_name THEN c.group_name
ELSE g.name
END

simple conditional aggregation:
select
t.name,
count(*) as total,
sum(case when c.status = 'OK' then 1 else 0 end) as passed,
sum(case when c.status = 'OK' then test_value end) as score
from
test_groups t
left join test_cases c
on t.name = c.group_name
group by t.name
Fiddle

with all_test_cases as (select name, count(id) as cnt from (
select a.name, a.test_value, b.id, b.status from test_groups a
left join test_cases b on a.name = b.group_name) total_test
group by name),
passed_test_cases as (
select name, count(name) as cnt from (
select a.name, a.test_value, b.id, b.status from test_groups a
join test_cases b on a.name = b.group_name
and b.status = 'OK') OK_test
group by name)
select tg.name, atc.cnt as all_test_cases , coalesce(ptc.cnt, 0) as passed_test_cases, coalesce(ptc.cnt * tg.test_value, 0) as total_value
from test_groups tg
left join all_test_cases atc on tg.name = atc.name
left join passed_test_cases ptc on tg.name = ptc.name
order by total_value desc, name

Select t.name,
sum(case when c.status = 'OK' or c.status = 'ERROR' then 1 else 0 end) as all_test_cases,
sum(case when c.status = 'OK' then 1 else 0 end) as passed_test_cases,
sum(case when c.status = 'OK' then 1 else 0 end) * t.test_value as total_value
from
test_groups t
left join test_cases c
on t.name = c.group_name
group by t.name,t.test_value
order by ,t.total_value,t.name

WITH A AS (SELECT group_name AS name, COUNT(status) AS all_test_cases,
SUM(CASE
WHEN status = 'OK' THEN 1
ELSE 0
END) AS passed_test_cases
FROM test_cases
GROUP BY group_name)
SELECT g.name, IFNULL(c.all_test_cases,0),
IFNULL(c.passed_test_cases,0),
IFNULL(g.test_value*c.passed_test_cases,0) AS total_value
FROM test_groups AS g
LEFT JOIN A AS c
ON g.name = c.name
GROUP BY g.name
ORDER BY total_value DESC;

Select Name Name_Of_The_Group ,Count(Status) All_Test_Cases,
Sum(Case When Status = 'OK' Then 1 Else 0 End) passed_test_cases ,
Sum(Case When Status = 'OK' Then test_value End) total_value
From Test_Cases Full Outer Join Test_Groups On Name = Group_Name
Group By Group_Name,Name
order by Name_Of_The_Group;

Try the following query:
select t.name,
count(c.*)as total,
sum(case when c.status='OK'then 1 else 0 end) as passed,
Case when sum(case when c.status='OK'then test_value end)Is NULL then 0
Else sum(case when c.status='OK'then test_value end)End as total_value
from test_groups t
left join test_cases c on t.name=c.group_name
group by t.name
order by total_value desc,t.name;

Related

Add flag for row existence in another table with group by

For below query I want to have a flag called isHold that will evaluate to 0
if there is no billNo from the view viewBills exists in onHold table and
1 otherwise
select max(t.id) TrackingID , max(vb.billNo) billNo, cb.id ,
max(case when vb.[count] > 1 then 1 else 0 end) isMultiple ,
max(case when t.TrackingID = 31 then 1 else 0 end) IsCancelled,
max(case when exists (select 1 from OnHold oh
where oh.billNo = billNo) then 1 else 0 end) IsHold
from viewBills vb
join tracking t on vb.billNo = t.billNo
join customerBills cb on vb.billNo = cb.billNo
join customerPieces cp on cb.id = cp.customerBillId
where cb.statusid <> 3
group by cb.id
I got this error when executing
Cannot perform an aggregate function on an expression
containing an aggregate or a subquery.
It's reasonable but how can achieve that?
You can use outer apply or a left join to move the logic to the FROM clause:
select max(t.id) as TrackingID , max(vb.billNo) as billNo, cb.id ,
max(case when vb.[count] > 1 then 1 else 0 end) as isMultiple,
max(case when t.TrackingID = 31 then 1 else 0 end) as IsCancelled,
max(case when oh.billNo is not null then 1 else 0 end) as IsHold
from viewBills vb join
tracking t
on vb.billNo = t.billNo join
customerBills cb
on vb.billNo = cb.billNo join
customerPieces cp
on cb.id = cp.customerBillId outer apply
(select top (1) oh.*
from OnHold oh
where oh.billNo = cb.billNo
) oh
where cw.statusid <> 3
group by cb.id;
You can go for LEFT OUTER JOIN and do the aggregation as given below:
select max(t.id) TrackingID , max(vb.billNo) billNo, cb.id ,
max(case when vb.[count] > 1 then 1 else 0 end) isMultiple ,
max(case when t.TrackingID = 31 then 1 else 0 end) IsCancelled,
max(case when oh.billNo IS NOT NULL then 1 else 0 end) IsHold
from viewBills vb
join tracking t on vb.billNo = t.billNo
join customerBills cb on vb.billNo = cb.billNo
join customerPieces cp on cb.id = cp.customerBillId
LEFT OUTER JOIN OnHold oh ON oh.billNo = vb.billNo
where cb.statusid <> 3
group by cb.id

how to get a name from a group by id in sql

I have a table of customers groups and I would like to select the group's names (and another value called Notes) but group by doesn't allow me to do that even though the entire group has the same value of names and notes.
select cg.idCustomerGroup,
sum(case when c.type = 'child' then 1 else 0 end) as Children,
sum(case when c.type = 'adult' then 1 else 0 end) as Adults,
sum(case when c.type = 'senior' then 1 else 0 end) as Seniors
from CustomersGroups cg
inner Join CustomersInGroup cig
on cg.idCustomerGroup = cig.idCustomerGroup
inner Join Customers c
on c.idCustomer = cig.idCustomer
Group by cg.idCustomerGroup
You have to include the group's name and notes in the GROUP BY:
select cg.idCustomerGroup, cg.Name, cg.Notes,
sum(case when c.type = 'child' then 1 else 0 end) as Children,
sum(case when c.type = 'adult' then 1 else 0 end) as Adults,
sum(case when c.type = 'senior' then 1 else 0 end) as Seniors
from CustomersGroups cg
inner Join CustomersInGroup cig
on cg.idCustomerGroup = cig.idCustomerGroup
inner Join Customers c
on c.idCustomer = cig.idCustomer
Group by cg.idCustomerGroup, cg.Name, cg.Notes

How to get count of a column value returned using subquery?

I want to get all hrc_acct_num which are not present in acct_key column of STAGING_CUST_ACCT table. The last outer select column is throwing an error. How can I get count of a column returned using a subquery?
SELECT source_sys_cd,
Count(CASE
WHEN is_delete = 0 THEN 1
END) [DEL IS 0],
Sum(CASE
WHEN trans_amt = 0 THEN 1
ELSE 0
END) [STG $0 TXN CNT],
Count(CASE
WHEN hrc_acct_num NOT IN(SELECT DISTINCT acct_key
FROM staging_cust_acct) THEN
hrc_acct_num
END)
FROM staging_transactions (nolock)
GROUP BY source_sys_cd
ORDER BY source_sys_cd
You can do a LEFT JOIN to the sub query and then do a SUM when the value is null. acct_key
SELECT source_sys_cd,
Count(CASE
WHEN is_delete = 0 THEN 1
END) [DEL IS 0],
Sum(CASE
WHEN trans_amt = 0 THEN 1
ELSE 0
END) [STG $0 TXN CNT],
SUM(CASE WHEN T.acct_key is NULL THEN 1 else 0 END ) CountNotIN
FROM staging_transactions (nolock) s
LEFT JOIN (SELECT DISTINCT acct_key
FROM staging_cust_acct) t
s.hrc_acct_num = t.acct_key
GROUP BY source_sys_cd
ORDER BY source_sys_cd
Here's a simplified demo
You can short circuit the subquery with NOT EXISTS. It's more efficient than LEFT JOIN (SELECT DISTINCT, since you don't care about enumerating all the times it does exist.
SELECT source_sys_cd,
Count(CASE is_delete WHEN
WHEN is_delete = 0 THEN 1
END) [DEL IS 0],
Count(CASE
WHEN trans_amt = 0 THEN 1
END) [STG $0 TXN CNT],
Count(CASE
WHEN NOT EXISTS (SELECT 1
FROM staging_cust_acct
WHERE acct_key = hrc_acct_num) THEN 1
END)
FROM staging_transactions (nolock)
GROUP BY source_sys_cd
ORDER BY source_sys_cd

Combining unrelated queries into one query to produce counts

I would like a stored procedure to run daily that produces a report of counts.
For example, the .csv would look something like this:
Daily,1
Deaths,0
In-House EKG,4
In-House Xray,2
Suicidal Patients,12
HIV,0
Their individual queries look something like this:
-- Daily and Death Counts
select
SUM(CASE WHEN location != '[OUT]' THEN 1 ELSE 0 END) as 'Daily',
SUM(CASE WHEN death = 1 THEN 1 ELSE 0 END) as 'Deaths'
from
patient_data
-- In-House Tasks
select
SUM(CASE WHEN cat_id = 72 THEN 1 ELSE 0 END) as 'In-House EKG',
SUM(CASE WHEN cat_id = 73 THEN 1 ELSE 0 END) as 'In-House XRay',
from
organizer_tasks
-- Suicidal Patients
select
count(distinct(pid)) as 'Suicidal Inmates'
from
problems pr
inner join problem_list pl on pl.id = pr.problem_list_id
where
pr.status = 'open'
and pl.title like '%suicide%'
-- HIV
select
count(distinct(pid)) as 'HIV'
from
problems pr
inner join problem_list pl on pl.id = pr.problem_list_id
inner join patient_data pd on pr.pid = pd.pid
where
pr.status = 'open'
and pl.title like '%hiv%'
As you can see, each set of data comes from a different table, and has no relation. How can I accomplish my desired result set?
Thanks.
-- Daily and Death Counts
select * from (
select
SUM(CASE WHEN location != '[OUT]' THEN 1 ELSE 0 END) as 'Daily',
SUM(CASE WHEN death = 1 THEN 1 ELSE 0 END) as 'Deaths'
from
patient_data
) tmp unpivot (Number for Type in ([Daily], [Deaths])) t
union all
-- In-House Tasks
select * from (
select
SUM(CASE WHEN cat_id = 72 THEN 1 ELSE 0 END) as 'In-House EKG',
SUM(CASE WHEN cat_id = 73 THEN 1 ELSE 0 END) as 'In-House XRay'
from
organizer_tasks
) tmp unpivot (Number for Type in ([In-House EKG], [In-House XRay])) t
union all
-- Suicidal Patients
select 'Suicidal Inmates',
count(distinct(pid))
from
problems pr
inner join problem_list pl on pl.id = pr.problem_list_id
where
pr.status = 'open'
and pl.title like '%suicide%'
union all
-- HIV
select 'HIV',
count(distinct(pid))
from
problems pr
inner join problem_list pl on pl.id = pr.problem_list_id
inner join patient_data pd on pr.pid = pd.pid
where
pr.status = 'open'
and pl.title like '%hiv%'
Try this with Union which is form in one Query :
select
SUM(CASE WHEN location != '[OUT]' THEN 1 ELSE 0 END) as 'Daily'
from
patient_data
UNION ALL
select
SUM(CASE WHEN death = 1 THEN 1 ELSE 0 END) as 'Deaths'
from
patient_data
UNION ALL
-- In-House Tasks
select
SUM(CASE WHEN cat_id = 72 THEN 1 ELSE 0 END) as 'In-House EKG'
from
organizer_tasks
UNION ALL
select
SUM(CASE WHEN cat_id = 73 THEN 1 ELSE 0 END) as 'In-House XRay'
from
organizer_tasks
UNION ALL
-- Suicidal Patients
select
count(distinct(pid)) as 'Suicidal Inmates'
from
problems pr
inner join problem_list pl on pl.id = pr.problem_list_id
where
pr.status = 'open'
and pl.title like '%suicide%'

sql query with percent and aggregation functions

I'm trying to write a sql query where I need number of scores > 3 by county, then for counties on that list I need to produce a percentage of rooms with scores < 3. So I need three columns, County Name, # of scores > 3 by county, % of rooms with scores < 3 by county
SELECT County = c.Description, [Score > 3] = count(s.Score),
((select count(room.Name) where s.Score< 3) /( select count(room.Name) ) * 100)
FROM Sites AS s
inner join Profiles as p on s.Profile_Id = p.Id
inner join Counties as c on p.County_Id = c.Id
inner join Rooms as room on s.Id = room.Site_Id
where s.Score > 3
Group By c.Description
I think you are over complicating the issue, rather than subselects you can just limit the data returned by using the HAVING clause, then use CASE in the COUNT:
SELECT County = c.Description,
[Score > 3] = COUNT(CASE WHEN Sites.Score > 3 THEN 1 END),
[% Score < 3] = 100.0 * COUNT(CASE WHEN Sites.Score < 3 THEN 1 END) / COUNT(1)
FROM Sites
INNER JOIN Profiles
ON Sites.Profile_Id = Profiles.Id
INNER JOIN Counties
ON Profiles.County_Id = Counties.Id
INNER JOIN Rooms
ON Sites.Id = Rooms.Site_Id
GROUP BY c.Description
HAVING COUNT(CASE WHEN Sites.Score > 3 THEN 1 END) > 0;
Demo on SQL Fiddle
EDIT
SELECT County = c.Description,
[Score > 3] = COUNT(CASE WHEN Sites.Score > 3 THEN 1 END),
[% Score < 3] = 100.0 * SUM(CASE WHEN Sites.Score < 3 THEN 1 END) / COUNT(*),
[Score > 3] = SUM(CASE WHEN Sites.Score > 3 THEN RoomCount ELSE 0 END),
[% Score < 3] = 100.0 * SUM(CASE WHEN Sites.Score < 3 THEN RoomCount ELSE 0 END) / SUM(RoomCount)
FROM Sites
INNER JOIN Profiles
ON Sites.Profile_Id = Profiles.Id
INNER JOIN Counties
ON Profiles.County_Id = Counties.Id
INNER JOIN
( SELECT Site_Id, RoomCount = COUNT(*)
FROM Rooms
GROUP BY Site_Id
) Rooms
ON Sites.Id = Rooms.Site_Id
GROUP BY c.Description
HAVING COUNT(CASE WHEN Sites.Score > 3 THEN 1 END) > 0;
Use Cast
SELECT County = c.Description, [Score > 3] = count(s.Score),
( Cast(select count(room.Name) where s.Score < 3 ) as float / ( select count(room.Name) ) * 100)
FROM Sites AS s
inner join Profiles as p on s.Profile_Id = p.Id
inner join Counties as c on p.County_Id = c.Id
inner join Rooms as room on s.Id = room.Site_Id
where s.Score > 3
Group By c.Description
[Score > 3] = count(s.Score)
...
where s.Score > 3
Both statements are not needed. In fact your Where clause limits all operations to s.score >3, which is not ideal when you are also trying to pull data from scores <3.
If you are trying to count both the cases where s.Score>3 and where it is <3, you need to use the CASE statement
SELECT SUM(CASE WHEN s.score < 3 THEN 1 ELSE 0 END) AS Hiscores,
SUM(CASE WHEN s.score > 3 THEN 1 ELSE 0 END) /count(s.scores) AS percentLowScores
This should do it
SELECT c.Description County,
SUM(CASE WHEN s.score < 3 THEN 1 ELSE 0 END) AS Hiscores,
SUM(CASE WHEN s.score > 3 THEN 1 ELSE 0 END) /count(s.scores) AS percentLowScores
FROM Sites AS s
inner join Profiles as p on s.Profile_Id = p.Id
inner join Counties as c on p.County_Id = c.Id
inner join Rooms as room on s.Id = room.Site_Id
Group By c.Description