I have a table which including Student's name, course_id and score, the different students may have a different course and different score.
And now I need to output the academic standings for each student, here is the rule for the academic standings:
If a student takes only one course(count(course_id)=1), he/she will receive ‘Good’ if the score >= 50, and ‘Referral’ if the score < 50;
If a student takes more than one course (count(course_id)>=2), his/her academic standing would be ‘Probation’ if none of the score >=50, ‘Referral’ if 50% or less of the taken courses are score >=50, and ‘Good’ otherwise.
Table:
Student_name| course_id |score
Shirley Caws 55993 10
Lana Glendenning 56988 81
Michael 54880 80
Michael 54895 71
Sean Turle 56986 32
Sean Turle 56991 48
Sean Turle 56992 20
Damchu Tenzin 56215 40
Damchu Tenzin 56219 90
Blake Croll 57179 30
Blake Croll 57264 20
I have tried for an hour in writing "CASE WHEN" but fail to get the right answer.
SELECT student_name, (CASE WHEN .... THEN ELSE END) AS academic standings FROM table;
Expected results:
Student_name| Academic_standings
Shirley Caws Referral
Lana Glendenning Good
Michael Good
Sean Turle Probation
Damchu Tenzin Referral
Blake Croll Probation
Thanks!
I'd try something like this - count the classes first, then apply your logic:
; with CTE as (
Select StudentName
, Count(distinct CourseID) as TotalClasses
, Count(distinct case when score < 50 then CourseID end) as ClassesUnder50
From MyTable
Group By StudentName
)
Select StudentName
, Case when TotalClasses = 1 and ClassesUnder50 = 0 then 'Good'
when TotalClasses = 1 and ClassesUnder50 = 1 then 'Referral'
when TotalClasses > 1 and ClassesUnder50 = TotalClasses then 'Probation'
when TotalClasses > 1 and ClassesUnder50*1.0/TotalClasses >= 0.5 then 'Referral'
else 'Good' end as Standing
from CTE
You can use aggregation functions and conditional aggregation:
select student_name,
(case when count(*) = 1 and max(score) >= 50 then 'Good'
when count(*) = 1 then 'Referral'
when max(score) < 50 then 'Probation'
when sum(case when score >= 50 then 1 else -1 end) <= 0 then 'Referral'
else 'Good'
end) as academic_standing
from t
group by student_name;
Related
I have two tables: db.transactions and db.salesman, which I would like to combine in order to create an output that has aggregated sales before each salesman's hire date and after each salesman's hire date.
select * from db.transactions
index sales_rep sales trx_date
1 Tom 200 9/18/2020
2 Jerry 435 6/21/2020
3 Patrick 1400 4/30/2020
4 Tom 560 5/24/2020
5 Francis 240 1/2/2021
select * from db.salesman
index sales_rep hire_date
1 Tom 8/19/2020
2 Jerry 1/28/2020
3 Patrick 4/6/2020
4 Francis 9/4/2020
I would like to aggregate sales from db.transactions before and after each sales rep's hire date.
Expected output:
index sales_rep hire_date agg_sales_before_hire_date agg_sales_after_hire_date
1 Tom 8/19/2020 1200 5000
2 Jerry 1/28/2020 500 900
3 Patrick 4/6/2020 5000 300
4 Francis 9/4/2020 2900 1500
For a single sales rep, to calculate the agg_sales_before_hire_date is likely:
select tx.sales_rep, tx.sum(sales)
from db.transactions tx
inner join db.salesman sm on sm.sales_rep = tx.sales_rep
where hire_date < '8/19/2020' and sales_rep = 'Tom'
group by tx.sales_rep
PostGRESQL. I am also open to the idea of doing it into Tableau or Python.
Using CROSS JOIN LATERAL
select
sa.sales_rep, sa.hire_date,
l.agg_sales_before_hire_date,
l.agg_sales_after_hire_date
from salesman sa
cross join lateral
(
select
sum(tx.sales) filter (where tx.trx_date < sa.hire_date) agg_sales_before_hire_date,
sum(tx.sales) filter (where tx.trx_date >= sa.hire_date) agg_sales_after_hire_date
from transactions tx
where tx.sales_rep = sa.sales_rep
) l;
Use conditional aggregation:
select tx.sales_rep,
sum(case when tx.txn_date < sm.hire_date then sales else 0 end) as before_sales,
sum(case when tx.txn_date >= sm.hire_date then sales else 0 end) as after_sales
from db.transactions tx inner join
db.salesman sm
on sm.sales_rep = tx.sales_rep
group by tx.sales_rep;
EDIT:
In Postgres, you would use filter for the logic:
select tx.sales_rep,
sum(sales) filter (where tx.txn_date < sm.hire_date) as before_sales,
sum(sales) filter (where tx.txn_date >= sm.hire_date then sales) as after_sales
I have a query which gives me StudentId and PercentageScored in Exams which the Student attended, a student can attend multiple exams
StudentId | PercentageScored
101 82
102 57
101 69
103 71
103 42
Below is a sample query, my actual query looks similar to the below.
Select s.StudentId, m.[PercentageScored]
FROM dbo.[Student] S
Inner join dbo.[Marks] m
ON S.[StudentId] = m.[StudentId]
WHERE S.[StudentGroup] = 12 AND S.[Active] = 1
Now i need to add some logic so that my output looks like below
StudentId | FirstClass | SecondClass | ThirdClass
101
102
103
104
If the students PercentageScored is above 80% then 1st class, PercentageScored between 60 to 80 % then 2nd class,PercentageScored below 60% then 3rd class.. I need to give the counts, for a given student how many times he scored more than 80%, how many times between 60 - 80%, how many times below 60%
Using conditional aggregation:
SELECT
s.StudentId,
COUNT(CASE WHEN m.[PercentageScored] > 80 THEN 1 END) AS FirstClass,
COUNT(CASE WHEN m.[PercentageScored] > 60 AND
m.[PercentageScored] <= 80 THEN 1 END) AS SecondClass,
COUNT(CASE WHEN m.[PercentageScored] <= 60 THEN 1 END) AS ThirdClass
FROM dbo.[Student] s
INNER JOIN dbo.[Marks] m
ON s.[StudentId] = m.[StudentId]
WHERE
s.[StudentGroup] = 12 AND s.[Active] = 1
GROUP BY
s.StudentId;
I have the following data:
Year score count
2012 20 grade 2000
2005 20 grade 32
2005 40 grade 428
2006 60 grade 731
2006 60 grade 472
...
I would need to know what percentage of occasions when the score is 60 grade does it lead to a good score.
I should assume the word grade appears in the text the word grade (so in this case 60 grade).
My expected output would be the percentage of rows with only 60 grade based on counts.
How can I select this information by looking at values with 60 grade in Score and determine the percentage?
You could try using a condition aggregation
select count(*)
, sum(case when score like '60%' then 1 else 0 end) num_x_60
, (sum(case when score like '60%' then 1 else 0 ) / count(*))*100 perc
from my_table
I recommend using avg() for the percentage:
select avg(case when score like '60%' then 100.0 else 0 end) as percentage
from my_table
I am currently writing some SQL for a project. I would like to try practicing case expressions, and in this case, practicing grouping records by age.
Here's the table I'm working with: Person
Name | Age
-----------
Mike | 32
Angela | 11
Chris | 65
Nat | 20
Sandra | 45
Shane | 82
I want to use a case expression to group these these people by age. I'd like to divide people into
-18 and younger
-19 to 40
-41 and older
So far, I've been trying to write a case expression like this:
select name, age
from person
order by
(case
when age < 18 //group first
when (age >= 19 and age < 40 //group second
else //group third
end);
Obviously this isn't working. I've been trying to understand case expressions a bit better, can anyone give me some pointers?
Here's what you are trying to achieve.
select name, age
from person order by
case when age < 18 then 0 when age >=19 and age <=40 then 1 else 2 end
We can try using a CASE expression to form the age groups:
SELECT
Name,
Age,
CASE WHEN Age <= 18 THEN 'young'
WHEN Age <= 40 THEN 'middle'
ELSE 'older' END AS age_group
FROM yourTable
ORDER BY
Age;
Try this.
Select AgeGroup, count(distinct *) as
count_names
from(SELECT NAME, CASE WHEN
Age <=
18 THEN 'young (1-18)'
WHEN Age <= 40 THEN 'Middle(19-
40)'
ELSE 'older (40-above)' END as
AgeGroup) group by AgeGroup
I am trying to create a report that prints our home room counts in a readable format. We currently have an Excel Template that is manually filled out but I would like to automate this process within SQL so we can just pull the report from the database. Since this is public school data I won't post my sub-query or "real" data but I'll try to be as specific as possible.
I have a subquery that pulls student_ID, School_Title, Grade_Level, and HomeRoom that I named Data. For the purposes of this dicussion, let's assume Data is actually a table in my database. I have been working on using a pivot, but it doesn't seem to do what I need. We have multiple schools, each with multiple homerooms per grade level. There are different numbers of homerooms per grade level per school depending on the size of the school. (So School 1 might have only 2 Kindergarten homerooms, but School4 might have 4 homerooms for Kindergarten).
Data looks like this:
Student_ID School_Title Grade_Level HomeRoom
12345 School1 KA HR1
23456 School1 KP HR2
34567 School1 01 HR3
45678 School1 01 HR4
......
56789 School7 06 HR100
The query I am using right now is (in SQL Server 2012):
SELECT *
FROM Data
PIVOT (Count(student_ID)
FOR GRADE_LEVEL IN ([KA],[KP],[01],[02],[03],[04],[05],[06])
) as PivotStudents
ORDER BY School_Title
But what I get out is:
School_Title HomeRoom KA KP 01 02 03 04 05 06
School 1 HR1 25 0 0 0 0 0 0 0
School 1 HR2 0 26 0 0 0 0 0 0
School 1 HR3 0 0 19 0 0 0 0 0
School 1 HR4 0 0 21 0 0 0 0 0
....
School 7 HR100 0 0 0 0 0 0 0 27
What I want to get out is:
School_Title HR KA HR KP HR 01 HR 02...
School 1 HR1 25 HR2 26 HR3 19 HR5 23
School 1 HR4 21
...
School 7 HR91 19 HR92 19 HR93 20 HR98 21
School 7 HR94 17 HR95 23 HR96 18 HR99 24
School 7 HR97 22
I don't care which are grouped together "in a row". So, in my head, HR3 happened to be the first grade 01 homeroom found so it's in row 1. But HR4 is also a grade 01 homeroom at School 1 and is the next one found so it goes into row 2.
It is possible for a homeroom to have multiple grade levels too (though only in 2 cases but I need to account for it being possible).
So basically, is the output I'm going for even possible within SQL? Do I need to get out of the database into Crystal or something to be able to "place" the data where I want it to go or can I give SQL enough information to format it how I want.
Thanks!
DBs
You can do this using aggregates with case statements.. the key will be to add a DENSE_RANK column to your data to separate the aggregates into rows.
SELECT
[School_Title],
[HR] = MAX(CASE WHEN [Grade_Level] = 'KA' THEN [HomeRoom] END),
[KA] = COUNT(CASE WHEN [Grade_Level] = 'KA' THEN [Student_ID] END),
[HR] = MAX(CASE WHEN [Grade_Level] = 'KP' THEN [HomeRoom] END),
[KP] = COUNT(CASE WHEN [Grade_Level] = 'KP' THEN [Student_ID] END),
[HR] = MAX(CASE WHEN [Grade_Level] = '01' THEN [HomeRoom] END),
[01] = COUNT(CASE WHEN [Grade_Level] = '01' THEN [Student_ID] END)
-- etc
FROM
(
SELECT *,
DENSE_RANK() OVER (PARTITION BY [School_Title], [Grade_Level] ORDER BY [HomeRoom]) Rn
FROM [Data]
) d
GROUP BY [School_Title], [RN]
SQL FIDDLE
Similar to above by Jamie, but changed the Count to happen with the Data Subquery and used Max for all the case statements so...
Select
School_Title,
[HR1]=MAX(CASE WHEN [grade_level]='KA' THEN [HomeRoom] END),
[KA]=MAX(CASE WHEN [grade_level]='KA' THEN [Number] END),
[HR2]=MAX(CASE WHEN [grade_level]='KP' THEN [HomeRoom] END),
[KP]=MAX(CASE WHEN [grade_level]='KP' THEN [Number] END),
..... etc.
From (
select *, ROW_NUMBER() OVER (PARTITION BY School_Title, Grade_LEvel ORDER by HomeRoom) Rn
from (
select count(student_id) as Number, School_Title, Grade_Level, HomeRoom
from Data
group by School_Title, Grade_Level, HomeRoom) data2
GROUP BY School_Title, Rn
ORDER BY SCHOOL_Title
I need to add some IsNull somewhere along the line so that a school that has 4 homerooms of grade 1 but only 3 homerooms of grade 2 doesn't have NULL/NULL chilling in the last row for that school, but all in all, I can't believe this can actually be done in SQL!!!