How to select 'exceptions' in SQL? - sql

I have a table comments which contains a field student_id(foreign key to students table.
I have another table students
What I would like to do, is run a query that displays all students who have not made any comments. The SQL I have, only shows students who have made comments
SELECT studentID, email, first_name, last_name FROM "students" JOIN comments ON students.id = comments.student_id
How do I 'reverse' this SQL to show students who have NOT commented?

One method uses not exists:
select s.*
from students s
where not exists (select 1
from comments c
where s.id = c.student_id
);

You could do this:
SELECT studentID, email, first_name, last_name
FROM students
LEFT JOIN comments ON students.id = comments.student_id
WHERE comments.student_id IS NULL

select s.studentID, s.email, s.first_name, s.last_name from students s where s.id not in (select student_id from comments);

Related

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.

Getting student name from student table belonging to a section with less than 5 students enrolled

Hello there, I have a questions for you guys. You see enrollment and student table in images below. I want to run a query getting first and last name from student table but students should belong to a section with less than 5 enrolled students.
Does that make any sense? If i am not clear please do ask me a question. Any help with this is greatly appreciated.
Why access one table more than once, if you can save some work by using analytic functions?
select
first_name, last_name
from
(
select
s.first_name, s.last_name, count(*) over(partition by e.section_id) as enrollment_count
from
student s
join enrollment e using (student_id)
)
where
enrollment_count < 5;
This should work.
SELECT s.FIRST_NAME, s.LAST_NAME
FROM student s
WHERE s.STUDENT_ID IN (
SELECT e1.STUDENT_ID
FROM e1.enrollment
WHERE e1.SECTION_ID IN (
SELECT e2.SECTION_ID
FROM e2.enrollment
GROUP BY e2.SECTION_ID HAVING COUNT(DISTINCT e2.STUDENT_ID) < 5
)
)
select t2.first_name, t2.last_name
from table2 t2
inner join
(select student_id from table1 where section_id in
(select section_id from
(select section_id, count(student_id)
from table1
group by section_id
having count(student_id) < 5
)
)
)t1
on t1.student_id = t2.student_id;
Its not pretty, but it should work.

Select students not having any corresponding course

I have a simple problem but I want to use Not having. I have two tables: student_table, and student_course
STUDENT (
ID,
Name
)
STUDENT_COURSE (
ID,
student_ID,
course_ID
)
I want to select those student name that does not have any corresponding course_ID in the STUDENT_COURSE table, so I write
SELECT *
FROM STUDENT
LEFT JOIN STUDENT_COURSE ON STUDENT_COURSE.STUDENT_ID = STUDENT.ID
but how can I write Not having in the next?
To find STUDENT with no STUDENT_COURSE, you could use different methods. See this article for the comparisons:
using NOT EXISTS:
SELECT s.*
FROM STUDENT s
WHERE NOT EXISTS(
SELECT 1 FROM STUDENT_COURSE c WHERE c.STUDENT_ID = s.ID
)
using LEFT JOIN
SELECT
s.*
FROM STUDENT s
LEFT JOIN STUDENT_COURSE c
ON c.STUDENT_ID = s.ID
WHERE c.ID IS NULL
using NOT IN
SELECT *
FROM STUDENT
WHERE ID NOT IN(
SELECT STUDENT_ID FROM STUDENT_COURSE
)
Just Add [STUDENT_COURSE].[STUDENT_ID] IS NULL filter which will find the students not invloved in any course
Left Outer join will produce NULL values for the right table columns which don't have a match.
SELECT *
FROM [STUDENT]
LEFT JOIN [STUDENT_COURSE]
ON [STUDENT_COURSE].[STUDENT_ID] = STUDENT.ID
WHERE [STUDENT_COURSE].[STUDENT_ID] IS NULL

Count within a count

REVISED:
Okay, thanks to all of your input, I figured out what I was doing wrong (sorry guys). I am grabbing the courseID which counts as a section NOT an actual course. For me to grab the actual courseName, I have to go over to the Course Table.
So now I have StudentID from Enrollment and CourseNum from Course that need to be used to count. I'll try and work with what you guys have provided to see if I can come up with the results.
EDIT:
Here's my revised SQL. This provides me with the total courses for each student. I'm getting there:
SELECT Count(DISTINCT Course.courseNum), Grades.studentID
FROM Grades INNER JOIN
Course ON Grades.courseID = Course.courseID
GROUP BY Grades.studentID;
Final Code just in case those who care:
SELECT COUNT(NumCourses) FROM
(SELECT Count(DISTINCT Course.courseNum)AS NumCourses
FROM Grades INNER JOIN
Course ON Grades.courseID = Course.courseID
GROUP BY Grades.studentID
HAVING Count(DISTINCT Course.courseNum) = 1) a;
Try this:
select count(studentId), count(courseId) from enrolment group by courseId having (count(courseId) = 2);
To get the total number of students on 4+ courses, this is the SQL:
SELECT COUNT(CourseCount) AS CourseCount
FROM (
SELECT StudentID, COUNT(CourseID) AS CourseCount
FROM enrollment
GROUP BY StudentID
HAVING (COUNT(CourseID) >= 4)) AS T
It is much simpler to get the student count using a second query.
SELECT COUNT(NumCourses) AS NumStudents, NumCourses FROM (SELECT COUNT(courseID) AS NumCourses FROM enrollment GROUP BY courseID HAVING COUNT(courseID) = 4) As SomeTableAlias
select count(*)
from (select student_id from enrollment group by student_id having count(*) = 4)
The inner query gives you the IDs of the students who enrolled in exactly 4 courses then I count them.
I thought you wanted the number of students who are enrolled in exactly four courses.
If you want to count the number of students and the number of courses, do this:
SELECT * FROM (SELECT COUNT(*) AS NumStudents FROM (SELECT DISTINCT studendid FROM enrollment)), (SELECT COUNT(*) AS NumCourses FROM (SELECT DISTINCT courseid FROM enrollment))
SELECT COUNT(*) FROM
(SELECT COUNT(*) FROM enrollment
GROUP BY studentid HAVING COUNT(*) = 4)