SQL Join Retrieves more rows than expected - sql

Suppose I have the following tables
Students (StudentId int Pk, StudentName nvarchar)
Lectures (LectureId Pk, StartDate, EndDate)
Enrollment (StudentID, LectureID)
When I execute the following query:
select StudentID From Students
I get 8 rows..
And when i execute:
select S.StudentID From Students S join Enrollment En On S.StudentID = En.StudentID
I get 11 Rows
Why is that, and how to use join without retrieving extra rows?

Your join is fine.
This means that there is more than one Enrollment per Student - e.g. Students have enrolled in more than one lecture / series of lectures
Assuming that a student can only register for one lecture at a time, then you will also need to join to lecture and use the date fields
Also, if a student is not currently enrolled in anything, you will need to consider a left outer join.
So your query might then look like
SELECT S.StudentID FROM Students S
LEFT OUTER JOIN Enrollment En On S.StudentID = En.StudentID
INNER JOIN Lectures l ON en.LectureId = l.Lecture ID
WHERE getdate() BETWEEN l.StartDate and l.EndDate
But you would need to have rules in place to ensure that the student cannot concurrently register for more than one lecture (if that is indeed what you expected)
HTH

Use a LEFT JOIN
EDIT: nevermind, it is as said below/above/whatever. You must have students with multiple lectures/enrollments. You will need to group by student to get 8 rows again.
select S.StudentID From Students S
join Enrollment En On S.StudentID = En.StudentID
group by S.StudentID

Related

Finding out zero frequency values from a column with inner join

I have two tables named courses and teachers. I need to find the list of teachers who don't take any courses.
This is the query I wrote for getting the teacher who took the most numbered courses -
SELECT t.name AS teacher_name
, COUNT(c.teacher_id) AS courses_taken
FROM courses c
JOIN teachers t
ON c.teacher_id = t.id
GROUP
BY c.teacher_id
ORDER
BY courses_taken DESC
LIMIT 1;
By reversing this query I am getting the teacher's list who take minimum numbers of courses which is 1 but I need to find the list of teacher who don't take any courses.
Left Join and Where condition can help you.
SELECT *
FROM teachers t
LEFT
JOIN courses c
ON t.id = c.teacher_id
WHERE c.teacher_id IS NULL
Teachers don't usually "take" courses. They "teach" courses. Students "take" courses.
That said, not exists seems appropriate:
SELECT t.*
FROM teachers t
WHERE NOT EXISTS (SELECT 1
FROM courses c
WHERE c.teacher_id = t.id
);
If you think about this, it is almost a direct translation of your question: Get teachers where there is no course that the teacher teachers.

SQL- Find the students who are missing their grades in database

I have three tables in my database. the first one is students which has student_id as primary key. the second one is courses table which has course_id as primary key and at last I have a grades table which has id_student and id_course as foreign keys, and a grade field. I want to the get details of students, who are missing their grades in grades table.
I've searched on stack overflow but could not find the exact answer i am looking for. and what i have tried so far is this following query:
select st.student_id,
st.lname,
st.fname,
cs.course_id,
g.grade
from students st
join grades g
on g.id_student = st.student_id
join courses cs
on cs.course_id = g.id_course
where g.grade is null
If I try this same query without where condition i get 39 rows but i should get 40 because in my database there is one student who is missing grades in course_id 20.
** the missing thing is student_id, where course_id is 20 and grade doesnt exist for it.**
I think the join on grades requires two keys. If I assume that students should be taking all courses, then this would look like:
select st.student_id, st.lname, st.fname, cs.course_id,
g.grade
from students st cross join
courses c left join
grades g
on g.id_student = st.student_id and
g.id_course = c.course_id
where g.grade is null;
The CROSS JOIN generates all combinations of students and courses. The LEFT JOIN/WHERE filters out the ones with missing grades.
Because you use JOIN you are only getting the rows that exist in all 3 tables.
If you change the JOINS to LEFT OUTER JOIN you will see NULLS for the student without a grade.
See https://www.w3schools.com/sql/sql_join.asp
and many other links on the web
SELECT * FROM
(
select st.student_id,st.lname,st.fname,cs.course_id,g.grade
from students st
LEFT OUTER join grades g on g.id_student = st.student_id
LEFT OUTER join courses cs on cs.course_id = g.id_course
)SUB_Q WHERE SUB_Q.grade IS NULL
The inner query should return all students and the ones without a grade should have a grade of null in that. So the WHERE clause of the outer query should filter down to just those.
The join function for the grades table is only looking for results that match both the grades table and students table. Try a full outer join here to include all fields regardless of if they match on both tables. See code below:
SELECT
st.student_id, st.lname, st.fname, cs.course_id, g.grade
FROM students st
FULL OUTER JOIN grades g
ON g.id_student = st.student_id
FULL OUTER JOIN courses cs
ON cs.course_ID = g.id_course
WHERE g.grade IS NULL

SQLITE3 Inner Join Returning Too Many Tuples

I have 4 tables in sqlite3:
students(student_id, student_name)
instructors(instructor_id, instructor_name)
courses(course_id, course_name)
and
enrollments(enroll_id, student_id, instructor_id, course_id)
With the last 3 columns having a foreign key reference to the relevant columns in the first 3 tables
When I try to do a query that shows only the student’s name, instructor’s name and course name where an enroll_id is equal to 001 for example
SELECT students.student_name, instructors.instructor_name, courses.course_name
FROM users, instructors, courses INNER JOIN enrollments
WHERE enrollments.enroll_id = 001;
I'm getting a lot of data from all tables, but not a single tuple with just the relevant enroll data assigned to enroll_id 001. Any help is appreciated
Never use commas in the FROM clause. Always use proper, explicit JOIN syntax. The right way to express this query is:
SELECT s.student_full_name, i.instructor_name, c.course_name
FROM enrollments e JOIN
instructors i
ON e.instructor_id = i.instructor_id JOIN
courses c
ON e.course_id = c.course_id JOIN
students s
ON e.student_id = s.student_id
WHERE e.enroll_id = 001;
The use of table aliases is highly recommended. It makes the query easier to write and to read.
Try this:
SELECT students.student_full_name, instructors.instructor_name, courses.course_name
FROM users, instructors, courses, enrollments
WHERE enrollments.enroll_id = 001
and enrollments.student_id = users.student_id
and enrollments.instructor_id = instructors.instructor_id
and enrollments.course_id = courses.course_id;

What Join to use against 2 Tables for All Data

Hi I am looking to find out what join I would use if I wanted to join 2 tables together. I currently have a list of all students so 25 students to 1 class and the other table only shows 7 of those names with their test results.
What I would like is to have 1:1 join for the ones with the test results and the other ones without I would like to show them underneath so all in all I have 20 records.
If somebody could please advise on how I could achieve this please.
Thanks in advance.
It sounds like you want an OUTER JOIN.
For this example, we'll assume that there is a table named student and that it contains a column named id which is UNIQUE (or PRIMARY) KEY.
We'll also assume that there is another table named test_result which contains a column named student_id, and that column is a foreign key referencing the id column in student.
For demonstration purposes, we'll just make up some names for the other columns that might appear in these tables, name and score.
SELECT s.id
, s.name
, r.score
FROM student s
LEFT
JOIN test_result r
ON r.student_id = s.id
ORDER
BY r.student_id IS NULL
, s.score DESC
, s.id
Note that if student_id is not unique in test_result, there is potential to return multiple rows that match a row in student.
To get (at most) one row returned from test_result per student, we could use an inline view.
SELECT s.id
, s.name
, r.score
FROM student s
LEFT
JOIN ( SELECT t.student_id
, MAX(t.score) AS score
FROM test_result t
GROUP BY t.student_id
) r
ON r.student_id = s.id
ORDER
BY r.student_id IS NULL
, s.score DESC
, s.id
The expressions in the ORDER BY clause are designed to return the students that have matching row(s) in test_result first, followed by students that don't.
This is just a demonstration, and very likely excludes some important criteria, such as which test a score should be returned for. But without a sample schema and some example data, we're just guessing.
You are looking for a left outer join or a full outer join.
The left outer join will show all students and their tests if they have them.
select *
from Students as s
left outer join Tests as t
on s.StudentId = t.StudentId
The full outer join will show all students with their tests if they have them, and tests even if they do not have students.
select *
from Students as s
full outer join Tests as t
on s.StudentId = t.StudentId

How to fetch all recordsets which are linked through that table?

I have three tables, with these fields:
classes: class_id | name | grade
classes_students: class_id | student_id
students: student_id | name
Classes has a 1:n relationship with Students, so one class can have many students. I want to select all students of a particular class, where class_id is 5.
Could I just do something like this?
SELECT student.name FROM students student
LEFT JOIN classes_students link
ON link.class_id = 5
AND link.student_id = student.student_id
I'm not sure about if I need the join here and if that has to be a "LEFT JOIN"? What I want is only a record set containing the student names.
Use:
SELECT s.name
FROM STUDENTS s
JOIN CLASSES_STUDENTS cs ON cs.student_id = s.student_id
AND cs.class_id = 5
Alternately:
SELECT s.name
FROM STUDENTS s
JOIN CLASSES_STUDENTS cs ON cs.student_id = s.student_id
JOIN CLASSES c ON c.class_id = cs.class_id
WHERE c.class_id = 5
Because you want students only in a particular class, you'd use an INNER JOIN. You'd use a LEFT JOIN if you wanted say a list of all the students, and then LEFT JOIN to CLASSES_STUDENTS to know if any are taking classes (because those that aren't would have null values in the CLASSES_STUDENTS columns). I recommend looking at this link for the breakdown of various JOINs and their impact on the data you'll get back.
SELECT
student.name
FROM
students student
INNER JOIN
classes_students link
ON
link.student_id = student.student_id
WHERE
link.class_id = 5
Your query should actually work fine if you just change your join to INNER, but i much prefer using a where clause to filter the results rather than including it in the JOIN criteria, i think it's clearer.