SQL Server case statement in select clause - sql

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;

Related

SQL several characteristics query

I have a problem with my SQL query. I have operations with (specific ID for each one) and I have participants in those operations that can be (seller, facilitator, manager, assistant)
Table looks like:
ID Volume Participant
---------------------------
122 100 Sellers
122 100 Facilitator
123 50 Sellers
123 50 Manager
123 50 Facilitator
124 120 Sellers
124 120 Assistant
125 180 Manager
125 180 Sellers
125 180 Facilitator
I want to extract operations where, for example, seller and manager have participated. In this case, the seller and manager have participated in operations 123 and 125
SELECT ops.opsId, ops.opsvolume, tranche.participant
FROM ops
INNER JOIN tranche ON ops.opsID = tranche.opsId
WHERE tranche.participant = 'seller'
AND tranche.participant = 'manager'
But obviously the participants can not be two roles at the same time, it is the operation that has several roles, any suggestions?
There's not enough information in the question yet to fully answer it. But perhaps you can start with this:
SELECT * FROM ops WHERE ID IN (
SELECT ID
FROM ops
WHERE participant IN ('sellers', 'manager')
GROUP BY ID
HAVING COUNT(*) = 2
)
I would start by pivoting out new columns, after which you can simply use a WHERE clause
with q as
(
SELECT ops.opsId,
max(ops.opsvolume) Volume,
max(case when tranche.Participant = 'seller' then 1 else 0 end) HasSeller,
max(case when tranche.Participant = 'manager' then 1 else 0 end) HasManager
FROM ops
INNER JOIN tranche ON ops.opsID = tranche.opsId
GROUP BY ops.opsId
)
select opsId, Volume
from q
where HasSeller = 1
and HasManager = 1

SQL sum values for each ID

I have a dataset about trains, it's including a table for the customers information which is a number representing an age group and the amount of travellers for that age group.
The ID represents a location which has multiple departure times, which has multiple age groups.
The data looks something like this
StationID
Time of Departure
TravellerID
Amount of travellers
1
12:13
4001
30
1
12:13
4002
15
1
19:45
4001
10
1
19:45
4002
20
I want to sum the amount of travellers for each departure
I tried to code it this way:
SELECT StationID,[Time of Departure], sum(Amount)
FROM Train_Stations AS TS
INNER JOIN DepartureData AS DD
ON DD.FK_StationID = TS.PK_StationID
INNER JOIN CustomerInfo AS CI
ON CI.FK_StationID = TS.PK_StationID
GROUP BY StationID, [Time of Departure]
The result is like this:
StationID
Time of Departure
Amount
1
12:13
75
1
12:13
75
1
19:45
75
1
19:45
75
But I want it like this:
StationID
Time of Departure
Amount
1
12:13
45
1
19:45
30
Seems, you do something different.Based on your data query is correct
WITH CTE(StationID,DEPARTURE_TIME,TRAVELLERID,AMOUNT_OF_TRAVELLERS) AS
(
SELECT 1,CAST('12:13'AS TIME),4001,30 UNION ALL
SELECT 1,CAST('12:13'AS TIME),4002,15 UNION ALL
SELECT 1,CAST('19:45'AS TIME),4001,10 UNION ALL
SELECT 1,CAST('19:45'AS TIME),4002,20
)
SELECT C.StationID,C.DEPARTURE_TIME,SUM(AMOUNT_OF_TRAVELLERS)TOTAL_TRAVELLERS
FROM CTE AS C
GROUP BY C.StationID,C.DEPARTURE_TIME
You should specify the column as DD.StationID. It will return as an expected result.
SELECT DD.StationID,DD.[Time of Departure], sum(DD.Amount)
FROM Train_Stations AS TS
INNER JOIN DepartureData AS DD
ON DD.FK_StationID = TS.PK_StationID
INNER JOIN CustomerInfo AS CI
ON CI.FK_StationID = TS.PK_StationID
GROUP BY DD.StationID, DD.[Time of Departure]

Conditional Aggregation with multiple case and group by

The query below gives me average of case when QuoteStatusID = 6 but it I am having issues with associating the average by Street column.
QuoteTable
QuoteID
QuoteStateID
ProjectManager_userID
Shipping_AddressID
1
6
12
56
2
6
12
56
3
26
12
56
4
6
12
18
5
26
12
18
Shipping_AddressID
56: 338 Elizabeth St
18: 83 East St
select [User].UserID, [User].fname, [User].lname,[User].JobTitle, address.Street,
(select avg(case when QuoteStatusID = 6 then 1.0 else 0 end) as QuoteAccept
from Quote q
where ProjectManager_UserID = userId
) as AcceptanceRate
from [User]
join quote on [user].UserID=Quote.ProjectManager_UserID
join Address on quote.Shipping_AddressID=Address.AddressID
where userID in (select distinct ProjectManager_UserID from quote)
order by AcceptanceRate desc;
Current output 3/5 =0.60
userid
fname
Lname
Street
AcceptanceRate
12
Jon
Smith
338 Elizabeth St
0.6
12
Jon
Smith
83 East St
0.6
Desired output 2/3=0.66 AND 1/2=0.50
userid
fname
Lname
Street
AcceptanceRate
12
Jon
Smith
338 Elizabeth St
0.66
12
Jon
Smith
83 East St.
0.50
I think you don't need a sub-query. Just avg as part of the query you have and use group by to give you distinct users and addresses.
select U.UserID, U.fname, U.lname, U.JobTitle, A.Street
, avg(case when Q1.QuoteStatusID = 6 then 1.0 else 0 end) as QuoteAccept
from [User] U
inner join Quote Q on Q.ProjectManager_UserID = U.UserID
inner join [Address] A on A.AddressID = Q.Shipping_AddressID
group by U.UserID, U.fname, U.lname, U.JobTitle, A.Street
order by AcceptanceRate desc;
Note: Short aliases make a query more readable. And you don't need your where clause, since the join on Quote already ensures the same condition.
Can you simply amend your avg to be
select avg(case when QuoteStateID = 6 then 1.0 else 0 end) over(partition by Shipping_AddressId) as QuoteAccept
Edit
To still use as a subquery it will need correlating in the where clause on Shipping_AddressId also

How to output a value refer to another value?

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;

How to get the student academic progress?

course Table
course_code course_name credit_points_reqd
1 Comp Science 300
2 Soft Engineering 300
subject Table
subject_code subject_name credit_points
CS123 C Prog 15
CS124 COBOL 15
enrolment table
student_id student_name course_code subject_code Results
1 Lara Croft 1 CS123 70
1 Lara Croft 1 CS124 50
2 Tom Raider 2 CS123 60
2 Tom Raider 2 CS124 40
3 James Bond 1 CS123 NULL
3 James Bond 1 CS124 40
OUTPUT TABLE
student_name course_name credit_points_obt credit_points_reqd
Lara Croft Comp Science 30 300
Tom Raider Soft Engineering 15 300
I'm currently using TSQL. So here's the situation. I've prepared these tables to get the output like the way it i showed u up there. I need to calculate the credit points obtained. Credit points are achieved if the student receives > 50 for a subject they took. I want to ignore students that has not received any credit points at all (eg, James Bond is ignored as he has not achieved any points yet)
select student_name, course_name,credit_points_obt,credit_points_reqd
FROM enrolment (SELECT student_full_name, SUM(credit_points) AS credit_points_obt
FROM enrolment
GROUP BY student_id),
Totally stuck...I have no idea where to go now.
You can sum conditionally to get points for subject. If none are given result will be null, so you filter out those student/course pairs in having clause.
I've changed > 50 condition to >= 50 because your results contradict your requirements. Also, by the data I'd say that you have omitted student table for brewity, but if you haven't, it is a must.
Live test is # Sql Fiddle.
select enrolment.student_name,
course.course_name,
course.credit_points_reqd,
sum(case when enrolment.results >= 50
then subject.credit_points
end) credit_points_obt
FROM enrolment
inner join course
on enrolment.course_code = course.course_code
inner join subject
on enrolment.subject_code = subject.subject_code
group by enrolment.student_name,
course.course_name,
course.credit_points_reqd
having sum(case when enrolment.results >= 50
then subject.credit_points
end) is not null