SQL query get course number for certain student grades - sql

I'm working through some problems and I can't seem to get the expected results for this one. The question is below with what is in my code right now and also the expected results. If anyone help that would be great. I'm just trying to get a understanding on this and can't seem to get my head around what exactly this is asking as you can see my code I have now isn't close to what the expected result is as of right now. Also I added the schema this will show whats in what table if needed for your guys help.
Question:
List the course number of courses wherein students have received grades for every one of the possible defined grade types. Order by course number.
My code so far:
SELECT g.Student_id, g.Grade_type_code
FROM Grade g LEFT OUTER JOIN Section s
ON g.Section_id = s.Section_id
GROUP BY g.Student_id, g.Grade_type_code
ORDER BY g.Student_id;
Any help would be great, also here is the Schema.
DBMS: I'm using Oracle SQL Developer
Here is the Expected Result
COURSE_NO
----------
20
25
100
120
122
125
130
135
Note: The Chapter for this problem is based off using
LEFT OUTER JOIN
My Current results
STUDENT_ID GRADE_TYPE_CODE
---------- ---------------
102 FI
102 HM
102 MT
102 PA
102 QZ
103 FI
103 HM
103 MT
103 PA
103 QZ
104 FI
104 HM

Based on your ER diagram I believe this query should return a list of courses whose enrolled students have collectively received all of the grade types listed in the GRADE_TYPE table.
select s.course_no,
c.descr,
count(distinct g.grade_type_code) as num_grade_types
from grade g
join enrollment e
on g.student_id = e.student_id
and g.section_id = e.section_id
join section s
on e.section_id = s.section_id
join course c
on s.course_no = c.course_no
group by s.course_no, c.descr
having count(distinct g.grade_type_code) = (select count(grade_type_code)
from grade_type)
I didn't notice your expected result was only the course # (you can just get rid of the columns you don't want from the select list). Also the join to the COURSE table is only there to get the course description, so if you don't want the course description selected, you do not need that join.

You need to select COURSE_NO instead. And also use JOIN and not LEFT JOUTER JOIN.
Something like this:
select COURSE_NO from
(
SELECT distinct (s.COURSE_NO)
FROM Grade g JOIN Section s
ON g.Section_id = s.Section_id
)
ORDER BY s.COURSE_NO;

Related

Sum functions doesn't show total values in single row after grouping

I have to showcase the total hours worked for each employee that worked on different dates.
I can use group by and sum function but I have multiple columns to showcase and it gives the same result sum with multiple rows. Additionally, I don't want to write every column name in group by clause as its not proper way of query writing.
I managed to get employees working hours from different tables using joins but couldn't sum up them in single row as a record. However it shows sum in multiple rows.
The script I used to get all the data is given below:
Select e.employeecode, Isnull(e.PreferredName,e.FirstNames) + '
' + e.LastName as [Name], w.Position
, sum(h.TotalHours) over (partition by e.employeecode order by e.employeecode)[TotalHours]
, e.Salary, e.startdate
From employee e
Left join Appointment ap on ap.EmployeeCode = e.EmployeeCode
left join Work w on w.WorkCode = ap.workCode
left join HistoricalAllowance h on h.EmployeeCode = e.EmployeeCode
left join TransPerPaySequence TP on tp.PaySequence = h.PaySequence
Where tp.PeriodEnd between '2021-04-24' and '2021-05-07'
and h.AllowanceCode in ('99','1000') and
and ap.isactive ='1'
group by e.EmployeeCode
It looks right now after writing a script:
employeecode
Name
Position
TotalHours
Salary
StartDate
1234
Anna
employee-Com
63.45
500
40792
1234
Anna
employee-Com
486.45
500
40792
2345
Jacky
Manager
126.9
700
41395
2345
Jacky
Manager
961.05
700
41395
2345
Jacky
Manager
67.05
700
41395
3456
Mahato
HR
402.3
570
41933
3456
Mahato
HR
67.05
570
41933
3456
Mahato
HR
126.9
570
41933
3456
Mahato
HR
126.9
570
41933
The way I wanted
employeecode
Name
Position
TotalHours
Salary
StartDate
1234
Anna
employee-Com
549.9
500
40792
2345
Jacky
Manager
1155
700
41395
3456
Mahato
HR
723.15
570
41933
Please help and let me know what I am missing.
Thanks In advance
I can appreciate not wanting to list all the columns for the GROUP BY . . . but for a slightly different reason: If you can avoid the aggregation then the query is likely to be faster.
And SQL Server offers a convenient solution, using OUTER APPLY:
select e.employeecode,
coalesce(e.PreferredName, e.FirstNames) + ' ' + e.LastName as Name,
w.Position,
h.TotalHoursTotalHours, e.Salary, e.startdate
From employee e Left join
Appointment ap
on ap.EmployeeCode = e.EmployeeCode left join
Work w
on w.WorkCode = ap.workCode outer apply
(select sum(h.TotalHours) as TotalHours
from HistoricalAllowance h join
TransPerPaySequence TP
on tp.PaySequence = h.PaySequence
where h.EmployeeCode = e.EmployeeCode and
tp.PeriodEnd between '2021-04-24' and '2021-05-07' and
h.AllowanceCode in ('99','1000')
) h
where ap.isactive = '1';
Remove the OVER(...) clause and then put every other (non SUMmed) column mentioned in the SELECT, into the GROUP BY also
Adhering to your it's not proper way of query writing rule is tripping you up on this one
ps, I've a doubt that the posted query in your question even runs - SQL server is not, to my knowledge, one of the few databases that will allow you to limit group by columns to just those on which all other selected, non aggregated columns are functionally dependent. Your query as posted should give a "column x in the select list is invalid because it is not an aggregate/in the group by" error

Need Simple SQL direction

I'm trying to write a Ado SQL statement for my Access table and I'm getting the wrong results.
Employee Table
ID Name DriverID
1 Alex 1
2 Tom 2
3 Trevor 3
4 PHIL 0
5 Gina 4
Vehicle Table
ID PLATE EMPLOYEEID INSERVICE
1 123XYZ 1 N
2 456GFR 2 Y
3 TFV4FG 3 Y
4 F6GK7D 4 Y
5 GEY7GH 1 Y
I want result of All employes and to display the Vehcicle info if they are assigned to it.
Result should be
Name Plate
Alex GEY7GH
Tom 456GFR
Trevor TFV4FG
PHIL
Gina F6GK7D
SELECT Employee.ID, Employee.FirstName, Vehicles.Plate, Vehicles.InService
FROM Employee LEFT JOIN Vehicles ON Employee.ID = Vehicles.DriverID
WHERE (((Vehicles.InService)=True));
Does not display PHIL who is not assigned to a vehicle.
Just add the condition inside the join, making sure to use parentheses to avoid problems when joining with constants or anything but simple equals:
SELECT Employee.ID, Employee.FirstName, Vehicles.Plate, Vehicles.InService
FROM Employee LEFT JOIN Vehicles ON (Employee.ID = Vehicles.DriverID AND Vehicles.InService = True)
From the above tables, it looks like the DriverID Column in employee table aligns with the EmployeeID column in the vehicles table and the issue is the on clause in the join.
SELECT
Employee.ID
,Employee.FirstName
,Vehicles.Plate
,Vehicles.InService
FROM
Employee
LEFT JOIN Vehicles ON Employee.DRIVERID = Vehicles.EMPLOYEEID
WHERE
(((Vehicles.InService)=True));
Well, in a normal database, you would move the WHERE condition to the ON clause. But I don't think that MS Access supports this:
SELECT e.ID, e.FirstName, v.Plate, v.InService
FROM Employee as e LEFT JOIN
Vehicles as v
ON e.ID = v.DriverID AND v.InService = True;
An alternative is a subquery:
SELECT e.ID, e.FirstName, v.Plate, v.InService
FROM Employee as e LEFT JOIN
(SELECT v.*
FROM Vehicles as v
WHERE v.InService = True
) as v
ON e.ID = v.DriverID ;

Find records for students missing a class

I have two tables, one that flags a user as having passed a course, and a list of courses per job code. I'm trying to query to return a record for all users that are missing classes.
Here are the tables:
Attended
--------
empid jobcode classcode grade
555 1 100 A
555 1 101 A
444 2 200 A
JobClassCode
--------
jobcode classcode
1 100
1 101
1 102
2 100
2 200
3 300
3 301
I started with this query to find classes with a missing user:
select * from attended at
right outer join jobcodeclass jc on at.jobcode = jc.jobcode and at.classcode = jc.classcode
I then tried to take that to build a correlated subquery, but I don't see a way to return both the user ID and missing course ID:
select * from jobcodeclass oq where classcode in (select jc.classcode from attended at
right outer join jobcodeclass jc on at.jobcode = jc.jobcode
and at.classcode = jc.classcode and jc.jobcode = oq.jobcode
and oq.classcode = jc.classcode and empid is null)
Generate all the possible classes that each employee needs by joining on the jobcode. The see which ones the student attended:
select ej.empid, ej.jobcode, jss.classcode
from (select distinct empid, jobcode from attended) ej join
JobClassCode jcc
on jcc.jobcode = ej.jobcode left join
attended a
on a.empid= e.empid and a.jobcode = ej.jobcode and
a.classcode = jcc.classcode
where a.empid is null;
If you just need the employees, use select distinct ej.empid.

Row that should have a 0 count not showing

I'm working through a problem, working with SQL Oracle. I'm getting the right results beside a row that should be showing up that has a count of 0. The question is
Question:
For each section of the Project Management course, list the section ID, location and number of students enrolled. Sort by section ID.
My Code:
SELECT s.Section_Id, s.Location, COUNT(*) AS Enrolled
FROM Course c, Section s, Enrollment e
WHERE c.Course_No = s.Course_No
AND s.Section_Id = e.Section_Id
AND c.Description = 'Project Management'
GROUP BY c.Course_No, s.Location, s.Section_Id
ORDER BY s.Section_Id;
My Results:
SECTION_ID LOCATION ENROLLED
---------- ------------------- ----------
48 L211 4
119 L211 3
120 L214 2
Expected Results:
SECTION_ID LOCATION ENROLLED
---------- ------------------- ----------
48 L211 4
119 L211 3
120 L214 2
121 L507 0
So as you can see I'm missing the row with 0 enrolled on my results and can't seem to get that row to appear. Also you will notice that it is a section id and location that go with it for that project management class but it won't appear. I'm not sure what I'm doing wrong.
Any help would be great, also here is the Schema.
DBMS: I'm using Oracle SQL Developer
How about joining to the tables instead of using the WHERE clause:
SELECT s.Section_Id,
s.Location,
COUNT(e.Section_Id) AS Enrolled
FROM course c
LEFT JOIN section s
ON c.Course_No = s.Course_No
LEFT JOIN enrollment e
ON s.Section_Id = e.Section_Id
WHERE c.Description = 'Project Management'
GROUP BY c.Course_No,
s.Location,
s.Section_Id
ORDER BY s.Section_Id;
#OmniSquad you could use SQL provided by Linger to create LEFT OUTER JOIN and select records where is no enrolment and changes statement COUNT(e.Section_Id) AS Enrolled.
You need to outer join to course and enrollment otherwise you won't see sections that don't have any courses/enrollments. You can either use the ANSI syntax ...LEFT JOIN... etc or the old Oracle syntax using (+) against the columns of the deficient table:-
SELECT s.Section_Id, s.Location, COUNT(*) AS Enrolled
FROM Course c, Section s, Enrollment e
WHERE c.Course_No (+) = s.Course_No
AND s.Section_Id = e.Section_Id (+)
AND c.Description (+) = 'Project Management'
GROUP BY c.Course_No, s.Location, s.Section_Id
ORDER BY s.Section_Id;
These days I would use the ANSI syntax...

What Join should I use in SQL

I have four tables in my Database:
Person that contains ID(PK) and Name.
Person_Skill that contains ID(PK), PID(FK), Skill(FK) and SkillLevel(FK).
Skill that contains ID(PK) and SkillLabel.
SkillLevel that contains ID(PK) and Name.
Every skill has level from 0 to 7
Now I want to display all the skill that the person has(Include the skilllevel = 0)
Select
[dbo].Person.Name as Name,
[Skill].SkillLabel as SkillName,
[Person_Skill].[SkillLevel] as SkillLevel
From
([dbo].Person inner join [dbo].[Person_Skill] ON [dbo].[Person_Skill].PID= Person.ID)
inner join [dbo].[Skill] ON [dbo].[Person_Skill].Skill=Skill.ID
The above code only display the skill that person has from level 1 to level 7.
I believe the reason I only get the skill from level 1 to 7 is because the person table only contains the skill from level 1 to 7, but I'm not sure about this. I got the database from other. If my assumption is correct, is there anyway to do this? To get all the skills in the skill table and display the skill level that the person has(Include skillllevel =0).
Sample Data:
Person
ID Name
----------
1 Michael
2 Alex
Person_Skill
ID PID SkillID Skill_Level
5 1 10 5
6 2 11 1
7 1 12 7
8 1 13 5
Skill
ID Name
10 java
11 C++
12 HTML
13 ASP
14 C
15 .NET
16 C#
17 Objective
The expect results are;
Name SkillName SkillLevel
----------------------------
Alex java 0
Alex C++ 1
Alex HTML 0
Alex ASP 0
Alex C 0
Alex .NET 0
Alex C# 0
Alex Objective C 0
Michael java 5
Michael C++ 0
Michael HTML 7
Michael ASP 0
Michael C 0
Michael .NET 5
Michael C# 0
Michael Objective C0
The current query only output
Alex C++ 1
Michael java 5
Michael HTML 7
Michael .NET 5
Edit, if you want to return all skill names for each person, then you will want to use:
select d.p_name,
d.s_name SkillName,
coalesce(ps.Skill_Level, 0) Skill_Level
from
(
select p.id p_id, p.name p_name, s.id s_id, s.name s_name
from person p
cross join skill s
) d
left join person_skill ps
on d.p_id = ps.pid
and d.s_id = ps.skillid
left join skill s
on ps.skillid = s.id
See SQL Fiddle with Demo
If you want to include all Skills 0-7, then you will want to use a LEFT JOIN. You query will be similar to the following:
select p.Name as Name,
s.SkillLabel as SkillName,
ps.[SkillLevel] as SkillLevel
from [dbo].[Skill] s
left join [dbo].[Person_Skill] ps
on ps.Skill=s.ID
left join [dbo].Person p
on ps.PID = p.ID
Edit, without seeing any data it is difficult to determine. But if you want to retrieve all SkillLevels, then you will need to include that table. You might need to use:
select
p.Name as Name,
s.SkillLabel as SkillName,
sl.[Name] as SkillLevel
from SkillLevel sl
left join [dbo].[Person_Skill] ps
on ps.SkillLevel=sl.ID
left join [dbo].[Skill] s
on ps.Skill = s.Id
left join [dbo].Person p
on ps.PID = p.ID
You would want to use a LEFT JOIN which when tableA is inner joined on tableb would return all records from tableA regardless of whether or not there was a match from tableB
Therefore, if there are no persons with a skill of 0, you will still get back all of the person records.
An INNER JOIN will only return rows where there is a match on both sides. So in your code if a person does not have a skill with level 0 it would not be returned.
You could do a LEFT or RIGHT join and these get all the rows from the table on either the left or the right side. I think you probably want to use a left join, but without knowing more about your schema it's hard to say for sure. See the answer given to the question Left join and Left outer join in SQL Server for more detail on the differences in different join types