Correlated Query with more than one table SQL - sql

I need to list some staff details who are registered for 2 or more courses using a sub query.
The staff_id in the staff table is linked to the course table, so s.staff_id = c.staff_id. But I'm really confused and don't know how to correct.
SELECT STAFF.STAFF_ID, STAFF.FIRST_NAME, STAFF.LAST_NAME, STAFF.TITLE, COURSE.ID
FROM STAFF, COURSE C
WHERE STAFF.STAFF_ID = COURSE.STAFF_ID
AND (SELECT COURSE.COURSE_ID FROM COURSE
GROUP BY STAFF.STAFF_ID
HAVING COUNT(COURSE.COURSE_ID) >=2);

I think that you don’t need a correlated subquery. An aggregate JOIN query with a HAVING clause should do it, like :
SELECT
S.STAFF_ID,
S.FIRST_NAME,
S.LAST_NAME,
S.TITLE
FROM
STAFF AS S
INNER JOIN COURSE AS C
ON S.STAFF_ID = C.STAFF_ID
GROUP BY
S.STAFF_ID,
S.FIRST_NAME,
S.LAST_NAME,
S.TITLE
HAVING
COUNT(*) >=2
;
This will retrieve all staff persons that have at least two courses.

If you want staff that have more than one course, a join is not necessary -- implicit or explicit. Here is a more direct approach:
select s.*
from staff s
where s.staff_id in (select c.staff_id
from course c
group by c.staff_id
having count(*) >= 2
);

You will need to link your sub query back to your main query. At the moment you are just looking for ANY staff member that has more than two courses in your sub query. Try the below (untested).
For clarity especially when you are using sub queries it is a good idea to alias all of your tables.
SELECT S.STAFF_ID, S.FIRST_NAME, S.LAST_NAME, S.TITLE
FROM STAFF S, COURSE C
WHERE S.STAFF_ID = C.STAFF_ID
AND (SELECT C1.COURSE_ID FROM COURSE c1
where s.staff_id = c1.staff_id
HAVING COUNT(C1.COURSE_ID) >=2)
The above assumes that you need to get some columns out of course as well, however at the moment you have none in your first select so the query could be further simplified to the below if you do not need anything from course.
SELECT S.STAFF_ID, S.FIRST_NAME, S.LAST_NAME, S.TITLE
FROM STAFF S
where (SELECT C.COURSE_ID FROM COURSE c
where s.staff_id = c.staff_id
HAVING COUNT(C.COURSE_ID) >=2)

If you want only staff details or else can join if needed course details as well
SELECT Staff.* from Staff s where
s.staff_id in
(Select
staff_id from
(SELECT COURSE_ID,STAFF_ID
FROM COURSE
GROUP BY STAFF_ID
HAVING COUNT(COURSE.COURSE_ID) >=2)
);
or via join infact even in join you require a subquery reason being group by when grouping by s.staff_id the c.course_id values will become inconsistent and unsync with s.staff_id
Select s.* ,c.COURSE_ID from staff s
join course c
on s.staff_id IN (Select c.staff_id group by
c.staff_id
HAVING COUNT(*) >=2)

Related

SQL: Need to check columns for values that exist in another column

Using SQL, my job is to fetch the SSN of students who enrolled in a course without enrolling in that course’s prerequisite(s). I'm using Access. The tables I need are as follows:
STUDENT (SSN, SNAME, MAJOR, DOB, ADDRESS)
ENROLLED (SSN, CID, GRADE)
PREQ (CID, PREQCID, PASSINGGRADE, NOTE)
Here's what I've done so far.
select *
from
(select SSN, CID from ENROLLED) AS enrolled
left join
(select CID, PREQCID FROM PREQ) AS prereq ON enrolled.CID = prereq.CID;
What I'm missing is how to check each row of the same student on the condition WHERE enrolled.CID = prereq.CID for a PREQCID that's NOT in enrolled.CID.
Is what I'm asking for here a loop? Am I on the right track? Please keep in mind this I'm in an introductory course so the simplest of solutions is preferable.
Here's another way using not exists:
select e1.ssn
from enrolled e1 left join preq p on e1.cid = p.cid
where
p.preqcid is not null and
not exists (select 1 from enrolled e2 where e2.ssn = e1.ssn and e2.cid = p.preqcid)
This is essentially stating:
"Select the ssn for all enrollments where there is a prerequisite course and the prerequisite course ID does not exist in the table of enrollments for that ssn."
I use select 1 purely for optimisation - we don't care about the values held by the nested query, only whether or not the nested query returns one or more records.
You could also write this using joins as:
select e1.ssn
from
(
enrolled e1 left join preq p on e1.cid = p.cid
)
left join enrolled e2 on
p.preqcid = e2.cid and e1.ssn = e2.ssn
where
e2.cid is null
Here, the enrolled table is referenced twice: the first is left joined on the table of prerequisite courses for each course, and the second is left joined on the prerequisite course ID and the ssn from the original enrollment.
The where clause then causes records to be selected for which the link on the prerequisite course is null for the given ssn.
I'm sure there is a cleaner way to do this, but this gets you your result:
Select S.SSN, E.CID From Student S
Inner Join Enrolled E on E.SSN = S.SSN
Where E.CID IN (Select CID From PREQ Where Preqcid NOT IN
(Select CID From Enbrolled Where SSN = S.SSN))
This might work if analytic functions and CTEs are available:
with d as (
select ssn, cid,
count(distinct p.cid) over (partition by e.ssn, e.cid) ecnt, ,
count(distinct e.cid) over (partition by e.ssn, p.cid) pcnt
from enrolled e left outer join preq p on p.cid = e.cid
)
select distinct ssn, cid from d
where ecnt = pcnt;

SQL on Oracle: Already joined two tables, now i want another column from another table(another join)

I have three tables;
1. Students - id, name
2. Subjects - sid,sname
3. Results - id,sid,marks (id and sid are foreign keys referenced by the two tables above)
Now, i perform
SELECT s.sname AS SubjectName, MAX(r.marks) AS MaxMarks
FROM subjects s, results r
WHERE s.sid=r.sid
GROUP BY r.sid, s.sname
ORDER BY r.sid
and i get The Subject Name with the maximum marks scored in them.
Now further, i also want the student name that has scored these max marks.
So i tried adding the column r.id, didn't work. I tried adding the table students in this query. I'm probably goofing up with the grouping after adding the table or something?
I did this
SELECT r.id AS StudentID, s.sname AS SubjectName, MAX(r.marks) AS MaxMarks
FROM subjects s, results r
WHERE s.sid=r.sid
GROUP BY r.sid, s.sname, r.id
ORDER BY r.sid
and i got each StudentID, with repeated subjects and the marks scored.
Whereas what i basically want is the student who has scored the highest in each subject.
you may use ROW_NUMBER() to tag the student who marked the highest on each subject
SELECT st.name,
sj.sname,
r.marks
FROM (SELECT id,
sid,
marks,
ROW_NUMBER() OVER (PARTITION BY sid
ORDER BY marks DESC) maxmarks
FROM results) r
JOIN students st
ON r.id = st.id
JOIN subjects sj
ON r.sid = sj.sid
WHERE r.maxmarks = 1
you can simple add the new join, and i suggest you the use of explicit join sintax
SELECT t.name, s.sname AS SubjectName, MAX(r.marks) AS MaxMarks
FROM subjects s
INNER JOIN results r ON s.sid=r.sid
INNER JOIN Students t ON t.id = r.id
GROUP BY r.sid, s.sname, t.name
ORDER BY r.sid
In the implicit join sintax should be
SELECT s.sname AS SubjectName, MAX(r.marks) AS MaxMarks
FROM subjects s, results r, stundet t
WHERE s.sid=r.sid
and t.id = r.id
GROUP BY r.sid, s.sname, t.name
ORDER BY r.sid

SQL not group by expression, Find student who has taken at least 5 courses

select s.name, s.id
from student s join takes t on t.id = s.id
where s.name like 'D%'
group by s.name, s.id
having (
select count(distinct c.course_id)
from course c
where c.dept_name = 'History' and c.course_id = t.course_id)>4
order by s.name
I am confused about how GROUP BY works. I am trying to find the students who has taken at least 5 courses from history department and name start with D.
Not sure with the nested subqueries...
course(course id, title, dept name, credits)
student(ID, name, dept name, tot_cred)
takes(ID, course_id, sec_id, semester, year, grade)
You have to additionally JOIN with course table:
select s.name, s.id
from student s
inner join takes t on t.id = s.id
inner join course c on c.course_id = t.course_id
where s.name like 'D%' and c.dept_name = 'History'
group by s.name, s.id
having count(distinct c.course_id) >= 5
The WHERE clause returns all students whose names start with a 'D' and have taken at least one course in history department. The HAVING clause filters out any students with 4 or less distinct courses in history department.

Selecting SQL data based on multiple separate tables

I'm doing some SQL practice and have been stumped by the following question.
I'm given the database schema:
Course (Course#, title, dept)
Student (Student#, name, program)
Enrolled (Student#, Course#, grade)
I'm trying the translate the following statement to SQL:
List the names of all students who takes Computer courses or Science courses.
Initially I thought the answer might be something like this:
SELECT Sname
FROM Course,Student,Enrolled
WHERE Course.dept = "Computer" OR Course.dept = "Science"
However, I feel like the rows in the table are not joined quite how I imagined, and that there is something off with this. How far off am I?
This is not that simple: first, you need to join the tables, and then you need to group by name to eliminate duplicates:
SELECT s.name
FROM Student s
JOIN Enrolled e ON s.Student#=e.Student#
JOIN Course c ON e.Course#=c.Course#
WHERE c.dept = 'Computer' OR c.dept = 'Science'
GROUP BY s.name
GROUP BY is necessary because the same student may be taking both "Computer" and "Science" courses, in which case JOIN would produce multiple records for the same student. In this case you have an option of replacing it with DISTINCT.
If you have 2 courses Computing (1) and Science (2) with the IDs 1 and 2, you need to do a query like this:
SELECT s.first_name, s.last_name FROM students s JOIN enrolled e ON e.student_id = s.id WHERE e.course_id IN(1, 2)
Sorry may have misread, if you need to do it by course type and the courses are tagged as dept = Computer, Science, Literacy etc... Do the following query:
SELECT s.first_name, s.last_name FROM students s JOIN enrolled e ON e.student_id = s.id JOIN courses c ON c.id = e.course_id WHERE c.dept IN('Computing', 'Science')
Or if you want to do an OR:
SELECT s.first_name, s.last_name FROM students s JOIN enrolled e ON e.student_id = s.id JOIN courses c ON c.id = e.course_id WHERE c.dept = 'Computing' OR c.dept = 'Science'

Finding total participation in sql

This is a question I could not answer in oracle lab exam.
Given the schema:
(Courses: cid(int), deptid(int)...);
(Students: sid(int), sname (string), deptid(int)...);
(Participation: cid(int), sid(int), ...);
A student can attend courses outside his department.
Need to get the names of the students who take all the courses offered by his department.
How to do this in sqlplus?
SELECT s.sid, s.sname, s.deptid
FROM Students s
INNER JOIN Participation p
ON s.sid = p.sid
INNER JOIN Courses c
ON p.cid = c.cid
AND s.deptid = c.deptid
GROUP BY s.sid, s.sname, s.deptid
HAVING COUNT(DISTINCT c.cid) = (SELECT COUNT(*)
FROM Courses c2
WHERE c2.deptid = s.deptid)
I cannot tesst the query right now, so I don't know if I have a syntactic error, anyway, you can try this idea to achieve your requirements.
SELECT studentName
FROM
(SELECT stu.sname AS studentName,
cour.deptid AS dept,
COUNT(*) AS assistedCoursesByDpt,
Max(cour.total) AS total
FROM students stu,
participation part,
(SELECT cour.deptid,COUNT(*) AS total FROM courses cour GROUP BY cour.deptid
) AS cour
WHERE stu.sid=part.sid
AND part.cid =cour.cid
GROUP BY stu.sid,
cour.deptid
)
WHERE total=assistedCoursesByDpt
Th idea is to create a subquery (cour) that has a new calculated column, the total courses by debt. Then you can compare this total with the agrouped student courses by dept.