How to combine table and aggregated results in SQL - sql

I have a table of indicators (e.g. number of students) for schools in a number of districts.
I need to aggregate those indicators by districts, and I would like to collate those aggregates at the bottom of my initial table:
I have :
district school students
District1 School1 10
District2 School2 20
District1 School3 30
District2 School4 40
District1 School5 50
District2 School6 60
I would like to eventually get in the same table:
district school students
District1 School1 10
District2 School2 20
District1 School3 30
District2 School4 40
District1 School5 50
District2 School6 60
District1 Total 90
District2 Total 120
I have tried
SELECT district, school, students
FROM enrolment
UNION
SELECT district, "Total" as school, sum(students)
FROM enrolment
GROUP BY district;
but I'm actually pulling many indicators from many different queries, and aggregating on two additional levels (states and country), so it's very slow. Is there a most efficient way of doing this?

If you are using SQL-Server you can use ROLLUP:
SAMPLE DATA:
CREATE TABLE #enrolment(district VARCHAR(25)
, school VARCHAR(25)
, students INT);
INSERT INTO #enrolment
VALUES
('District1'
, 'School1'
, 10),
('District2'
, 'School2'
, 20),
('District1'
, 'School3'
, 30),
('District2'
, 'School4'
, 40),
('District1'
, 'School5'
, 50),
('District2'
, 'School6'
, 60);
QUERY:
SELECT district
, ISNULL(school, 'Total') AS school
, SUM(students) AS students
FROM #enrolment
GROUP BY ROLLUP(district, school);
RESULT:

Related

Arranged in ascending order excluding one item

I have this table
Here I want to arranged table in ascending order order by firstname and those who are from UK by customer id. I tried doing this
select *
from tblCustomers
order by FirstName, LastName, age asc
where not country = 'uk'
but we can't do it as where clause should be used after table name and when we do
select *
from tblCustomers
where not country = 'uk'
order by FirstName, LastName, age asc
It exclude UK which is not my desired solution. How can I arranged in ascending order, order by firstname and firstname from UK from customer Id?
Build the order by clause up using case expressions e.g.
declare #tblCustomers table (customer_id int, first_name varchar(12), last_name varchar(12), age int, country varchar(3));
insert into #tblCustomers (customer_id, first_name, last_name, age, country)
values
(1, 'John', 'Doe', 31, 'USA'),
(2, 'Robert', 'Luna', 22, 'USA'),
(3, 'David', 'Robinson', 22, 'UK'),
(4, 'John', 'Reinhardt', 25, 'UK'),
(5, 'Betty', 'Doe', 28, 'UAE');
select *
from #tblCustomers
order by
case when country = 'UK' then 1 else 0 end asc -- non-UK first
, case when country = 'UK' then customer_id else null end asc -- UK ordered by id
, first_name, last_name, age asc; -- Non-UK ordered as specified
Returns:
customer_id
first_name
last_name
age
country
5
Betty
Doe
28
UAE
1
John
Doe
31
USA
2
Robert
Luna
22
USA
3
David
Robinson
22
UK
4
John
Reinhardt
25
UK
Notes:
please don't use images as we can't copy and paste the data
ideally provide the DDL+DML (as I have shown) as this makes it much easier to answer
always ensure your question is consistent, with the same table and columns names throughout
always provide your desired results
don't store age in a database, store date of birth and calculate age.

How do I calculate the percent of the total per year when COUNT and WHERE is used?

In SQL Server, I have a query
SELECT season, COUNT(DISTINCT player_name) AS 'No. of Foreign Players'
FROM nbastats
WHERE country <> 'USA'
GROUP BY season
It return these results
id
season
No. of Foreign Players
1
1996-97
9
2
1997-98
14
3
1998-99
22
4
1999-00
24
5
2000-01
40
6
2001-02
51
7
2002-03
62
What I'm trying to do is to instead get the percentage of foreign players (over total players) each season. The database only provides "country" so I assume I can only use
WHERE country <> 'USA'
and perhaps divide the total but I am unsure how to with WHERE in the way. Any help would be greatly appreciated!
I think you want a ratio of a conditional:
SELECT season,
COUNT(DISTINCT CASE WHEN country <> 'USA' THEN player_name END) * 1.0 / COUNT(DISTINCT player_name) AS foreign_ratio
FROM nbastats
GROUP BY season

Calculate percentage of students SQL

How to calculate percentage of students with higher mark than average for each course?
Assume I have a table (avg_marks) with average marks for each course and number of students in the course:
course_id avg_mark num_students
12345 74 20
12346 70 17
12347 64 33
...
I also have a table (enrolments) with all courses, students enrolled in those courses and their mark for the course:
course_id student mark
12345 1010 63
12345 2111 75
12345 3221 85
12345 6788 40
...
12347 8989 90
...
The expected output would be the table with course id and percentage of students with higher marks than average:
course_id percentage
12345 40
12345 20
12346 50
...
I have calculated number of students who have higher mark than average, but somehow I wasn't able to calculate the percentage (perhaps because the table contains all courses?). How can I modify this query or make a new one to calculate the percentage of students with higher mark than average?
Number of students with higher than average mark:
SELECT e.course_id, COUNT(e.student)
FROM enrolments e, avg_mark av
WHERE e.mark > av.avg_mark AND e.course_id=av.course_id
Output of the above query was like the following:
course_id count
12345 5
12346 10
12347 8
...
You don't need the table avg_marks.
Use window function AVG() in enrolments to get the average mark for each course_id and then use conditional aggregation with AVG() aggregate function to get the percentage:
SELECT course_id,
ROUND(100 * AVG((mark > avg_mark)::int)) percentage
FROM (SELECT *, AVG(mark) OVER (PARTITION BY course_id) avg_mark FROM enrolments) e
GROUP BY course_id
See the demo.
select
e.course_id,
count(case when e.mark > av.avg_mark then e.student end)/count(*) * 100 as students_cnt_with_mark_above_avg_pct
from enrolments e, avg_mark av
where e.course_id = av.course_id
group by e.course_id

SQL - Calculate Incentive using Frequency (Mode)

I’d like a SQL query to calculate the incentive column and based on the number of times a student was tutored in a month.
If the student was tutored in AP Math twice in a month then, the Tutor gets $20, else $0.
I don’t want group the date into month summary so, I’d like to leave the dates as is hence why I assigned $10 for each record where the students that were tutored twice in month.
I think you just need window functions:
select t.*,
(case when cnt = 2 then 10 else 0 end) as incentive
from (select t.*,
count(*) over (partition by studentid, subject, tutor, year(studentvisitdate), month(studentvisitdate) as cnt
from t
) t
initializing the data
declare #StudentTable as table
(
StudentVisitDate date,
StudentId int,
StudentName varchar(100),
[Subject] varchar (30),
Tutor varchar(100),
Incentive int
)
insert into #StudentTable
values
('2018-August-03',123,'Terry Brooks','AP Math','Shawn Green',10)
,('2018-August-04',123,'Terry Brooks','AP Science','Ted Berry',10)
,('2018-August-07',123,'Terry Brooks','Music','Shawn Green',10)
,('2018-September-03',123,'Terry Brooks','AP Math','Shawn Green',10)
,('2018-September-04',123,'Terry Brooks','AP Science','Ted Berry',10)
,('2018-September-07',123,'Terry Brooks','AP Math','Shawn Green',10)
getting the count of visits per month, grouping the data
;with temp as
(
select
StudentId, Subject, Tutor,Month(StudentVisitDate) [month]
,max(StudentVisitDate) maxdate -- the date on which the incentive will be calculated
,Count(StudentVisitDate) [count]
from #StudentTable
Group by
StudentId, Subject, Tutor,Month(StudentVisitDate)
)
getting the effective incentives for each month using the information from above table
select
s.StudentVisitDate ,
s.StudentId ,
s.StudentName ,
s.[Subject] ,
s.Tutor ,
Case
when t.maxdate = s.StudentVisitDate -- the incentive will be applied on the maximum date
then
s.Incentive*t.count
else
0
end Incentive
from #StudentTable s
inner join temp t
on s.StudentId=t.StudentId
and s.Subject =t.Subject
and s.Tutor=t.Tutor
and Month(s.StudentVisitDate)=t.month
order by StudentVisitDate
Final output -
StudentVisitDate StudentId StudentName Subject Tutor Incentive
2018-08-03 123 Terry Brooks AP Math Shawn Green 10
2018-08-04 123 Terry Brooks AP Science Ted Berry 10
2018-08-07 123 Terry Brooks Music Shawn Green 10
2018-09-03 123 Terry Brooks AP Math Shawn Green 0
2018-09-04 123 Terry Brooks AP Science Ted Berry 10
2018-09-07 123 Terry Brooks AP Math Shawn Green 20

Calculate in a table with different cells - SQL Server / T-SQL

I have in my database (SQL Server 2008 R2) a table like this:
ID......Team...........Name......Age
102 Barcelona Mike 15
103 Barcelona Peter 10
104 Barcelona Jacke 10
105 Barcelona Jonas 10
106 Real Madrid Michael 20
107 Real Madrid Terry 26
108 Chelsea James 26
109 Chelsea Arthur 23
110 Chelsea Spence 22
How can I loop the field 'Team' and know that, there are records like Barcelona, Real Madrid and Chelsea.
After that I want to calculate the sum of the team player of each team.
For Barcelona: -> 10 + 10 + 10 + 15 = 45
For Real Madrid: -> 20 + 26 = 46
For Chelsea: -> 26 + 23 + 22 = 71
Fill each result in a separate variable.
The whole calculation should be done in a stored procedure.
The second thing, if I have a table like this:
ID......Team...........Name......HeaderGoal......FreeKickGoal
104 Barcelona Mike 2 1
105 Barcelona Peter 1 0
106 Real Madrid Michael 0 1
107 Real Madrid Terry 0 1
108 Chelsea James 0 0
109 Chelsea Arthur 2 3
110 Chelsea Spence 4 0
How can I loop the field 'Team' and know that, there are records like Barcelona, Real Madrid and Chelsea.
After that I want to calculate the sum of all Goals of each team with the goal type HeaderGoal and FreeKickGoal.
Example for
-> Barcelona: 2+1+1 = 4
-> Real Madrid: 1+1 = 2
-> Chelsea: 2 + 3 + 4 = 9
Fill each result in a separate variable.
The whole calculation should be done in a stored procedure.
I hope you can help me!
BK_
If I understood your question correctly it looks like what you want are aggregates for each group, something that is easily accomplished with the GROUP BY clause.
For the first query you would use:
SELECT team, SUM(age) AS 'Sum of the team'
FROM table
GROUP BY team
This will give this result:
Team Sum of the team
-------------------- ---------------
Barcelona 45
Chelsea 71
Real Madrid 46
and for the second:
SELECT team, SUM(headergoal + freekickgoal) AS 'Sum of goals'
FROM table
GROUP BY team
which will give this result:
Team Sum of goals
-------------------- ------------
Barcelona 4
Chelsea 9
Real Madrid 2
In your example data you list the desired result for the first part for Chelsea as 45 but I guess that is just a typo as you omitted one of Chelseas rows in the calculation?
As for turning it into a stored procedure I can just tell you that it's easy and refer you to the documentation as I won't do all the work for you...
Edit: added merge intoas a response to a comment:
To insert the result of the second query into an existing table you can use either a simple INSERT statement like this:
INSERT table_with_team_and_goals
SELECT team, SUM(headergoal + freekickgoal)
FROM table
GROUP BY team
or MERGE INTO which might be better if you intend to run the query many times (the target table will then be updated if the team already exist in it):
MERGE INTO table_with_team_and_goals AS target
USING (SELECT Team, SUM(headergoal + freekickgoal) AS goals FROM table_with_goals GROUP BY team) AS source
ON target.team=source.team
WHEN MATCHED THEN
UPDATE SET goals = source.goals
WHEN NOT MATCHED THEN
INSERT (Team, Goals)
VALUES (source.team, source.goals);
SELECT TEAM , Name, COUNT(TEAM) As GoalsPerTeam, COUNT(NAME) As GoalPerPlayer
FROM TABLE
GROUP BY TEAM , Name
This query will give you tolal goals scored by per player and per team .
-- Sum age by team
SELECT Team, SUM(Age) SumAge
FROM
(
SELECT Id, Team, Name, Age FROM
(
VALUES
(102, 'Barcelona' , 'Mike' , 15),
(103, 'Barcelona' , 'Peter' , 10),
(104, 'Barcelona' , 'Jacke' , 10),
(105, 'Barcelona' , 'Jonas' , 10),
(106, 'Real Madrid', 'Michael', 20),
(107, 'Real Madrid', 'Terry' , 26),
(108, 'Chelsea' , 'James' , 26),
(109, 'Chelsea' , 'Arthur' , 23),
(110, 'Chelsea' , 'Spence' , 22)
) AS X(Id, Team, Name, Age)
) X
GROUP BY Team
-- Sum goals by team
SELECT Team, SUM(HeaderGoal + FreeKickGoal) Goals
FROM
(
SELECT Id, Team, Name, HeaderGoal, FreeKickGoal FROM
(
VALUES
(104, 'Barcelona' , 'Mike' , 2, 1),
(105, 'Barcelona' , 'Peter' , 1, 0),
(106, 'Real Madrid', 'Michael', 0, 1),
(107, 'Real Madrid', 'Terry' , 0, 1),
(108, 'Chelsea' , 'James' , 0, 0),
(109, 'Chelsea' , 'Arthur' , 2, 3),
(110, 'Chelsea' , 'Spence' , 4, 0)
) AS X(Id, Team, Name, HeaderGoal, FreeKickGoal)
) X
GROUP BY Team