What Join to use against 2 Tables for All Data - sql

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

Related

Select A column from Subquery

I have a query like below
Select Student_ID, Name, School
From Student S
Where S.Student_ID in ( select associate_id from Details D)
The output count is coming as expected
But now I have a new requirement to get additional column (D.Subject)in the data from the Details table
Select S.Student_ID, S.Name, S.School, D.Subject
From Student S
Where S.Student_ID in ( select associate_id from Details D)
When I’m trying to achieve the above by using the join like below the count is not matching . I tried both Left outer and inner join and the count doesn’t come out correctly .
Select S.Student_ID, S.Name, S.School, D.Subject
From Student S
Left outer join Details D on S.Student_ID = D.associate_id
Where S.Student_ID in ( select associate_id from Details D)
Please let me know how to achieve this
Since we dont have the sample data, I tried to put something together based on your question
problem is with the distinct count.
You should use distinct values in select. Dont really need the where condition, as that will be covered in your join condition.
Select distinct S.Student_ID, S.Name, S.School, D.Subject
From Student S join Details D on S.Student_ID = D.associate_id;
this will give you the unique counts there are. As there may be more join conditions between these tables and not just the column student_id to associate_id
for ex-
If i take the example below
and
in line query will give you a different count then inner join.
So either you find a correct join conditions or distinct the values to keep you close.

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

Query to find students that failed all given subjects

I'm trying to find the students that have failed every subject in a set of subjects via PostgreSQL queries.
Students fail a subject if they have a not null mark < 50 for at least one course offering of the subject. And I want to find the students that have failed all subjects in the set of subjects Relevant_subjects.
NOTE: students can have several records per course.
SELECT People.name
FROM
Relevant_subjects
JOIN Courses on (Courses.subject = Relevant_subjects.id)
JOIN Course_enrolments on (Course_enrolments.course = Courses.id)
JOIN Students on (Students.id = Course_enrolments.student)
JOIN People on (People.id = Students.id)
WHERE
Course_enrolments.mark is not null AND
Course_enrolments.mark < 50 AND
;
With the code above, I get the students that has failed any of the Relevant_subjects but I my desired result is to get the students that has failed all Relevant_subjects. How can I do that?
Students fail a subject if they have a not null mark < 50 for at least one course offering of the subject.
One of many possible ways:
SELECT id, p.name
FROM (
SELECT s.id
FROM students s
CROSS JOIN relevant_subjects rs
GROUP BY s.id
HAVING bool_and( EXISTS(
SELECT -- empty list
FROM course_enrolments ce
JOIN courses c ON c.id = ce.course
WHERE ce.mark < 50 -- also implies NOT NULL
AND ce.student = s.id
AND c.subject = rs.id
)
) -- all failed
) sub
JOIN people p USING (id);
Form a Carthesian Product of students and relevant subjects.
Aggregate by student (s.id) and filter those who failed all subjects in the HAVING clause with bool_and()over a correlated EXISTS subquery testing for at least one such failed course for each student-subject combination.
Join to people as final cosmetic step to get student names. I added id to get unique results (as names probably are not guaranteed to be unique).
Depending on actual table definition, your version of Postgres, cardinalities and value distribution, there may be (much) more efficient queries.
It's a case of relational-division at its core. See:
How to filter SQL results in a has-many-through relation
And the most efficient strategy is to eliminate as many students as possible as early in the query as possible - like by checking the subject with the fewest failing students first. Then proceed with only the remaining students etc.
Your case adds the specific difficulty that the number and identities of subjects to be tested are unknown / dynamic. Typically, a recursive CTE or similar offers best performance for this kind of problem:
SQL query to find a row with a specific number of associations
I would use aggregation:
SELECT p.name
FROM Relevant_subjects rs JOIN
Courses c
ON c.subject = rs.id JOIN
Course_enrolments ce
ON ce.course = c.id JOIN
Students s
ON s.id = ce.student JOIN
People p
ON p.id = s.id
WHERE ce.mark < 50
GROUP BY p.id, p.name
HAVING COUNT(*) = (SELECT COUNT(*) FROM relevant_subjects);
Note: This version assumes that students only have one record per course and relevant_subjects has no duplicates. These can easily be handling using COUNT(DISTINCT) if necessary.
To handle duplicates, this would look like:
SELECT p.name
FROM Relevant_subjects rs JOIN
Courses c
ON c.subject = rs.id JOIN
Course_enrolments ce
ON ce.course = c.id JOIN
Students s
ON s.id = ce.student JOIN
People p
ON p.id = s.id
WHERE ce.mark < 50
GROUP BY p.id, p.name
HAVING COUNT(DISTINCT rs.id) = (SELECT COUNT(DISTINCT rs2.id) FROM relevant_subjects rs2);

SQL Group by not returning accurate answer

it is current result
select cat.sms_schoolcategoryid as categoryId,
cat.sms_name as category ,
count(sch.sms_name) as schoolname,
count(stu.accountnumber)NoofStudent
from Filteredsms_schoolcategory cat
inner join Filteredsms_school sch
on cat.sms_schoolcategoryid=sch.sms_schoolcategoryid
inner join FilteredAccount stu
on sch.sms_schoolid=stu.sms_schoolid
group by cat.sms_schoolcategoryid,
cat.sms_name
;
I have three tables one is Category and 2nd is Schools and 3rd is students. i just want to count the schools on behalf of category when I join tables category and school it returns me accurate result and when i join students table with schools table it returns me wrong result. Please Guide me how it is possible.
There is guesswork involved unless you provide more information on you data model. However, it appears that your issue is rooted in the following:
When you join the student table, for each combination of school category and school (*) you generate additional records. I.e., your sql no longer counts schools by school category but students.
For a concrete solution and a good advice see #Nick.McDermaid's comment.
Try this one
select count(stu.accountnumber) as NoofStudent
, catsch.categoryId
, catsch.category
, catsch.schoolname
from FilteredAccount stu
inner join (
select cat.sms_schoolcategoryid as categoryId
, cat.sms_name as category
, count(sch.sms_name) as schoolname
, sch.sms_schoolid
from Filteredsms_schoolcategory cat
inner join Filteredsms_school sch
on cat.sms_schoolcategoryid = sch.sms_schoolcategoryid
group by cat.sms_schoolcategoryid, cat.sms_name, sch.sms_schoolid) catsch
on catsch.sms_schoolid = stu.sms_schoolid
group catsch.categoryId
, catsch.category
, catsch.schoolname
count() returns the number of non-NULL values. So, your two count() will return the same values. You can quickly fix the query using count(distinct):
select cat.sms_schoolcategoryid as categoryId,
cat.sms_name as category ,
count(distinct sch.sms_name) as schoolname,
count(distinct stu.accountnumber) as NoofStudent
from Filteredsms_schoolcategory cat inner join
Filteredsms_school sch
on cat.sms_schoolcategoryid = sch.sms_schoolcategoryid inner join
FilteredAccount stu
on sch.sms_schoolid = stu.sms_schoolid
group by cat.sms_schoolcategoryid, cat.sms_name ;
Actually, you probably don't need the second count distinct. Just count(stu.accountnumber) should count the students.

SQL Count and Group by - Do count and group, used together, allow for this?

i'm working on an exercise query, using standard SQL.
What I need, in this example, is get the Name and the ID of every student who has enrolled in any course more than 2 times. In this case, the tables have the following information (I'll list only the columns that matter for this exercise):
Student has the ID and the name of the student.
Course has the course ID.
Class has the group ID and the course ID.
Registration has the reg. ID, the student ID, the class ID and the grade obtained.
So, it all sums up to find out which students have more than 2 entries in Registration that are matched to the same course, via Class. So far I've gotten this:
SELECT Student.id, Student.name FROM
Student S JOIN Registration R on S.id = R.studentID
WHERE (SELECT COUNT(*) FROM
Course C JOIN Class L on L.courseId = C.id
JOIN Registration R on R.classId = L.id
group by C.id ) > 2
The question is, do Count and Group by work this way?? Do they allow me to get the amount of matches on each group, or do they just give me the results on the set, as I fear they do?
If so, any ideas on how may I approach this problem??
Thanks for the help!!
Start reading up on the HAVING clause. Also, you aliases Student as S, so you need to use the prefix "S" for the SELECT clause, not "Student"
SELECT S.id StudentID, S.name, C.id CourseID
FROM Student S
JOIN Registration R on S.id = R.studentID
JOIN Class L on R.ClassId = L.id
JOIN Course C on L.courseId = C.id
GROUP BY S.id, S.name, C.id
HAVING COUNT(R.studentID) > 2;
All 4 tables are joined to produce a resultset representing all students registrated for any class of a course. Then we GROUP BY the student-course combination, and find out the ones where there are more than 2 registrations using the HAVING clause. Of course, if you meant "2 or more" instead of "more than 2", you'd use > 1 or >= 2 instead of > 2.