SQL counting with condition - sql

If I have a table called Buildings.
Room_No Bldg Capacity
112 SCEN 23
242 JBHT 25
542 SCEN 4
324 JBHT 24
What I want is to print out the Bldg name and the total number of rooms that have a capacity more than 20 in each building. So it is supposed to look like:
Bldg Total
SCEN 1
JBHT 2
Am I going on the right track:
Select Bldg, Count(Capacity > 20) as Total from Buildings Group By Total Desc

You could use CASE:
Select Bldg, Count(CASE WHEN Capacity > 20 THEN 1 END) as Total
from Buildings
Group By Bldg
ORDER BY Total DESC;
If you are using Postgresql you could rewrite it as:
Select Bldg, Count(1) FILTER(WHERE Capacity>20) as Total
from Buildings
Group By Bldg
ORDER BY Total DESC;
Rextester Demo

The other answers seem overly complicated for this problem. The solution is rather simple:
SELECT Bldg, COUNT(*) AS count
FROM Buildings
WHERE Capacity > 20
GROUP BY Bldg
Here is the Fiddle: http://sqlfiddle.com/#!9/308a6/1

Related

Calculate every participant's points, based on winners result in age group

The winner of the competition stage in the respective age group is the participant who made the distance fastest. Each participant in the respective group receives for each stage points. The points obtained are calculated by dividing the time of the group winner by the time of the participant and multiplied by 1000. The total score is calculated by the sum of the points of the 5 best stages.
I have participant data like this.
AgeGroup Start_Nr First_name Last_Name Distance_result
-------------------------------------------------------------
M30 5 John Lala 180
M35 1 Paul Baba 175
M35 6 Patric Ziza 192
M30 3 Peter Mikel 190
S30 2 Sandra Lilua 250
S30 4 Julia Parker 260
And I want to calculate and display point like this
Age_Group|Start_Nr|First_name|Last_Name|Distance_result|Points
----------------------------------------------------------
M30 5 John Lala 180 1000
M30 3 Peter Mikel 190 947
M35 6 Patric Ziza 175 1000
M35 1 Paul Babas 185 946
S30 2 Sandra Lilua 250 1000
S30 4 Julia Parker 260 962
Each winner in age group gets 1000points, others in that age group get points calculating -> (MIN(Distance_result)/(Distance_result) * 1000)
SELECT [Age_group],
[Start_number] ,
[First_name],
[Last_name],
[Stage_Nr],
[Distance_result], (180/[Distance_result]*1000) AS Points,
DENSE_RANK() OVER (PARTITION BY [Age_group] ORDER BY [Distance_result] ASC) AS PlaceRank
FROM [ParticipantDetails].[dbo].[ParticipantForm]
How can I use MIN(Distance_result) in each age group to do point calculations?
In my solution I can calculate points only inserting MIN(Distance_result) manually, but even then it's not correct to other age groups. In every age group there is a different best result.
In my Example I have solved your problem with a subquery.
I have also added round to remove the decimals and before that I have added cast to get some result, otherwise I would receive only 1 or 0, or you can do this: p1.[Distance_result] instead of cast...
SELECT [AgeGroup],
[Start_Nr] ,
[First_name],
[Last_name],
[Distance_result],
(select round(min(p2.Distance_result)/p1.[Distance_result] * 1000, 0) AS Points
from ParticipantForm p2
where p2.AgeGroup = p1.AgeGroup
group by p2.AgeGroup) as Points,
DENSE_RANK() OVER (PARTITION BY [AgeGroup] ORDER BY [Distance_result] ASC) AS PlaceRank
FROM [ParticipantForm] p1
Here you can see the demo.
You can use a sub-query to get the best times and join the table to it.
From a performance point of view it is preferable to have a sub-query in the join which is run only once than a row-level sub-query which is run for every line.
We avoid the need to cast as float and use round() by doing the multiplication by 1000 before the division.
SELECT
[Age_group],
[Start_number] ,
[First_name],
[Last_name],
[Stage_Nr],
[Distance_result],
(b.best*1000)/[Distance_result AS Points,
DENSE_RANK() OVER (PARTITION BY [Age_group] ORDER BY [Distance_result] ASC) AS PlaceRank
FROM [ParticipantDetails].[dbo].[ParticipantForm]
JOIN ( SELECT [Age_group] AgeGroup,
MIN([Distance_result]) AS best
FROM [ParticipantDetails].[dbo].[ParticipantForm]
GROUP BY [Age_group]) AS b
ON b.[AgeGroup] = [ParticipantForm].[Age_group];
Try calculating the minimum Distance_Result partitioned by Age_Group. Then calculate the points.
WITH cte AS (
SELECT *
, DENSE_RANK() OVER (PARTITION BY [Age_Group] ORDER BY [Distance_Result] ASC) AS Place_Rank
, MIN(Distance_Result) OVER (PARTITION BY [Age_Group] ORDER BY [Distance_Result] ASC) AS Min_Distance
FROM [ParticipantForm]
)
SELECT [Age_group]
, [First_Name]
, [Last_Name]
, [Start_Number]
, [Distance_Result]
, CAST(ROUND( [Min_Distance] * 1000.0 / [Distance_Result], 0 ) AS INT) AS Points
FROM cte
Results:
Age_group
First_Name
Last_Name
Start_Number
Distance_Result
Points
M30
John
Lala
5
180
1000
M30
Peter
Mikel
3
190
947
M35
Paul
Baba
1
175
1000
M35
Patric
Ziza
6
192
911
S30
Sandra
Lilua
2
250
1000
S30
Julia
Parker
4
260
962
db<>fiddle here

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 Query Problem Involving (SUM, Group By, Order by, I guess? and maybe total, or even count)

By using SQL query, find out the Top 5 highest total Transaction Value, which Industry are they? and the number of stores in that industry?
My SQL data looks like this:
Store Name
Industry
Transaction Value
Ace
A
196
Ace
A
193
Area
A
168
Apple
A
165
Boy
B
145
Boy
B
143
Bull
B
136
Bread
B
131
Cat
C
116
Cat
C
106
Cake
C
104
Candy
C
102
Dog
D
101
Dog
D
92
Door
D
80
Daddy
D
75
Egg
E
70
Egg
E
67
Earl
E
66
Eagle
E
61
This is just for your reference, Top 5 highest Transaction Value are:
No.
Store Name
Industry
Total Transaction Value
1
Ace
A
389
2
Boy
B
288
3
Cat
C
222
4
Dog
D
193
5
Area
A
168
SQL Query Results should look something like this:
Industry
No. of Stores
A
2
B
1
C
1
D
1
E
0
select a.industry, sum(case when b.name is null then 0 else 1 end) as no
from
(select distinct industry from transactions ) a
left join
(select name, industry
from transactions
group by name, industry
order by sum(transaction_vaule) desc limit 5) b
on a.industry = b.industry
group by a.industry
order by a.industry
I think I have a solution for you. Please check my code I have used Common Table Expression ,CASE,SUM and group by =>
WITH CTE AS
(
SELECT industry, SUM(TransactionValue) AS Transaction_Value,
COUNT(StoreName) AS StoreCount FROM MYTable
GROUP BY StoreName,industry
ORDER BY SUM(TransactionValue) DESC
Limit 5
)
SELECT T1.industry,
SUM((CASE WHEN c.industry IS NULL THEN 0
ELSE 1 END)) as CT
FROM
(SELECT DISTINCT Industry FROM MYTable) AS T1
LEFT JOIN CTE as c ON T1.industry=c.industry
GROUP BY T1.industry
Note: Subquery is not best practice, but in your case, I think there will be no performance issue. Also, please check the code because, I do not have Snowflake SQL database installed, so there might be some syntactical error can be evident
.
To get a deterministic result, you must be aware of ties. Let's say the top 9 results are
Cat/A/600, Dog/A/500, Cat/B/500, Dog/B/400, Cat/C/300, Dog/C/300, Cat/D/300, Dog/D/200, Cat/E/100
Which is the top fifth? Cat/C/300 or Dog/C/300 or Cat/D/300? Or none of them? If we pick a row arbitrarily (by LIMIT 5 or FETCH FIRST 5 ROWS ONLY) we prefer one industry over another.
In standard SQL we have the clause FETCH FIRST 5 ROWS WITH TIES, but snowflake doesn't feature this, unfortunately. It does however feature DENSE_RANK. It ranks my sample rows thus:
#1: Cat/A/600
#2: Dog/A/500
#2: Cat/B/500
#3: Dog/B/400
#4: Cat/C/300
#4: Dog/C/300
#4: Cat/D/300
#5: Dog/D/200
#6: Cat/E/100
because the five top values are 600, 500, 400, 300, and 200.
The query:
select industry, count(case when rnk <= 5 then 1 end) as stores
from
(
select industry, dense_rank() over (order by sum(transaction_value) desc) as rnk
from mytable
group by store_name, industry
) ranked
group by industry
order by industry;
If you only want to show top industries:
select industry, count(*) as stores
from
(
select industry, dense_rank() over (order by sum(transaction_value) desc) as rnk
from mytable
group by store_name, industry
) ranked
where rnk <= 5
group by industry
order by industry;

i have a table like below i need which person got highest total?

name T E M S SS
nelson 10 20 30 40 50
felix 60 70 80 40 50
leon 60 30 80 90 10
i need name , total mark scored by three and who gets highest total
This below code will give you the total,
SELECT name, SUM(T+E+m+S+SS) as Total
FROM yourtable
Group by name
To get the highest scorer you could use order by and top 1,
Select top(1) *
From(SELECT name, SUM(T+E+m+S+SS) as Total
FROM yourtable
Group by name)a
Order by Total DESC
I think want something like this:
select name, (T + E + M + S + SS) as total
from t
order by total desc
fetch first 1 row only;
Note that not all databases support the standard fetch first clause. Some use select top or limit or even other mechanisms.

How to select students who got above average?

How to list all students who got above average grade of their group in SQL table? We have 6 group_ids so there six different average grades.
group_id student grade
1 James 85
1 Adam 96
2 Tom 56
2 Jane 89
2 Anny 90
Result:
group_id student grade
1 Adam 96
2 Jane 89
2 Anny 90
ashkufaraz's answer is closer but not quite right
select group_id,student,grade from students one where grade >
(select avg(grade) from students two where two.group_id = one.group_id)
The question is just tagged SQL, so this is an answer using standard SQL:
One option is to use a window function:
select group_id,student,grade
from (
select group_id,student,grade,
avg(grade) over (partition by group_id) as group_avg
from studends
) t
where grade > group_avg;
This has the additional benefit that you can also display the group average along with the result with no additional join or sub-select.