sql query with percent and aggregation functions - sql

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

Related

SQL query that summarizes each group

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;

How to use a conditional aggregate for total gross

I have this query:
SELECT CAST(co.DateCreated AS DATE) AS Date,
SUM(w.Gross),
SUM(CASE WHEN co.BookingSourceId = 1 THEN 1 ELSE 0 END) as Website,
SUM(CASE WHEN co.BookingSourceId = 2 THEN 1 ELSE 0 END) as Phone,
COUNT(*) as Total_Orders
FROM [Sterlingbuild].[dbo].[CustomerOrder] co
INNER JOIN ( SELECT Gross, CustomerOrderId, p.ProductBrandId
FROM [Sterlingbuild].[dbo].[CustomerOrderItem] coi
INNER JOIN [Sterlingbuild].[dbo].[Product] p ON coi.ProductId = p.ProductId
) w ON co.CustomerOrderId = w.CustomerOrderId
WHERE CustomerOrderStatusId = 7 AND DepartmentId = 1 AND w.ProductBrandId = 7
GROUP BY CAST(co.DateCreated AS DATE)
ORDER BY CAST(co.DateCreated AS DATE)
At the moment it returns the number of orders made by phone/website however I want the total GROSS for both phone/website how do I amend the query to achieve this.
Using SQL server
Sum the gross:
SUM(CASE WHEN co.BookingSourceId = 1 THEN w.gross ELSE 0 END) as Website_gross,
SUM(CASE WHEN co.BookingSourceId = 2 THEN w.gross ELSE 0 END) as Phone_gross,

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

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 Server Month Totals

SQL Server newbie
The following query returns SRA by Student and month only if there is a record for a student in Discipline table. I need a query to return all students and month totals even if there is no record for student in Discipline table. Any direction appreciated
SELECT TOP 100 PERCENT MONTH(dbo.Discipline.DisciplineDate) AS [Month], dbo.Discipline.StuId, dbo.Stu.Lastname + ',' + dbo.Stu.FirstName AS Student,
SUM(CASE WHEN Discipline.SRA = 1 THEN 1 END) AS [Acad Suspension], SUM(CASE WHEN Discipline.SRA = 2 THEN 1 END) AS Conduct,
SUM(CASE WHEN Discipline.SRA = 3 THEN 1 END) AS Disrespect, SUM(CASE WHEN Discipline.SRA = 4 THEN 1 END) AS [S.R.A],
SUM(CASE WHEN Discipline.SRA = 5 THEN 1 END) AS Suspension, SUM(CASE WHEN Discipline.SRA = 6 THEN 1 END) AS Tone
FROM dbo.Discipline INNER JOIN
dbo.Stu ON dbo.Discipline.StuId = dbo.Stu.StuId
GROUP BY dbo.Discipline.StuId, dbo.Stu.Lastname, dbo.Stu.FirstName, MONTH(dbo.Discipline.DisciplineDate)
ORDER BY Student
You need to change the INNER JOIN onto dbo.Stu to a LEFT JOIN:
SELECT MONTH(d.disciplinedate) AS [Month],
d.StuId,
s.Lastname + ',' + s.FirstName AS Student,
SUM(CASE WHEN d.SRA = 1 THEN 1 END) AS [Acad Suspension],
SUM(CASE WHEN d.SRA = 2 THEN 1 END) AS Conduct,
SUM(CASE WHEN d.SRA = 3 THEN 1 END) AS Disrespect,
SUM(CASE WHEN d.SRA = 4 THEN 1 END) AS [S.R.A],
SUM(CASE WHEN d.SRA = 5 THEN 1 END) AS Suspension,
SUM(CASE WHEN d.SRA = 6 THEN 1 END) AS Tone
FROM dbo.Discipline d
LEFT JOIN dbo.Stu s ON s.stuid = d.stuid
GROUP BY d.StuId, s.Lastname, s.FirstName, MONTH(d.DisciplineDate)
ORDER BY Student
The LEFT JOIN means that whatever table you're LEFT JOINing to might not have records to support the JOIN, but you'll still get records from the base table (dbo.Discipline).
I used table aliases - d and s. Less to type when you need to specify references.
generate a series of months, join discipline to that.