How to make a query from 4 tables - sql

I am having a problem creating a View using SQL. I need to make a View of 4 tables:
tbl_school, tbl_teacher, tb_student, and tbl_class.
This is my table structure:
And this is my View Statement
SELECT
tbl_school.school_id,
tbl_school.school_nm,
(SELECT COUNT(*) FROM tbl_class) AS class,
(SELECT COUNT(*) FROM tbl_teacher) AS teacher,
(SELECT COUNT(*) FROM tbl_student) AS student
FROM
tbl_school
INNER JOIN tbl_teacher ON tbl_school.school_id = tbl_teacher.school_id
INNER JOIN tbl_class ON tbl_teacher.teacher_id = tbl_class.teacher_id AND tbl_school.school_id = tbl_class.school_id
INNER JOIN tbl_student ON tbl_class.class_id = tbl_student.class_id
GROUP BY
tbl_school.school_id
And this is the query result:
The problem is that I have one teacher in SD1 School and another teacher in SD2 School. Each teacher has one class and SD1 School has two students and SD2 School has one student.
Is there a way I can get the results that I desire?

You can use an aggregation containing DISTINCT keywords, and had better using aliasing and one more column (tbl_school.school_nm) within the GROUP BY list to make it a more proper SQL( Btw some DBMS don't allow excluding that column from GROUP BY while MySQL allows ) :
SELECT s.school_id, s.school_nm,
COUNT(DISTINCT c.class_id) AS class,
COUNT(DISTINCT t.teacher_id) AS teacher,
COUNT(DISTINCT d.student_id) AS student -- this is a presumedly existing column within the student table
FROM tbl_school s
JOIN tbl_teacher t ON s.school_id = t.school_id
JOIN tbl_class c ON t.teacher_id = c.teacher_id AND s.school_id = c.school_id
JOIN tbl_student d ON c.class_id = d.class_id
GROUP BY s.school_id, s.school_nm

Welcome to SO.
It has been a while since I have done this, but have you tried adding a WHERE modifier to your internal SQL select statements? Like this...
Side note: It makes more sense, to me, to also have a FK on tbl_student that links them to which school that they're in.
SELECT
tbl_school.school_id,
tbl_school.school_nm,
(SELECT COUNT(*) FROM tbl_class WHERE school_id=tbl_school.school_id) AS class,
(SELECT COUNT(*) FROM tbl_teacher WHERE school_id=tbl_school.school_id) AS teacher,
(SELECT COUNT(*) FROM tbl_student) AS student
FROM
tbl_school
INNER JOIN tbl_teacher ON tbl_school.school_id = tbl_teacher.school_id
INNER JOIN tbl_class ON tbl_teacher.teacher_id = tbl_class.teacher_id AND tbl_school.school_id = tbl_class.school_id
INNER JOIN tbl_student ON tbl_class.class_id = tbl_student.class_id
GROUP BY
tbl_school.school_id

Related

The multi-part identifier could not be bound in SQL Server and it confused

I have this SQL query:
SELECT
stu.sno, sname, cname
FROM
sc scc,
(SELECT AVG(sc.grade) AS avg_grade
FROM sc
GROUP BY sc.cno) AS avg_grades
INNER JOIN
course c ON c.cno = scc.cno
INNER JOIN
s stu ON stu.sno = scc.sno;
And there is an error that the multi-part identifier scc.cno could not be bound. I'm confused - could someone help me?
Don't mix implicit and explicit joins! Matter of fact, don't use implicit joins: this is archaic syntax, that should not appear in new code.
The comma in the FROM clause should (probably) be a CROSS JOIN:
SELECT stu.sno, sname, cname
FROM sc scc
CROSS JOIN (SELECT AVG(sc.grade) AS avg_grade FROM sc GROUP BY sc.cno) AS avg_grades
INNER JOIN course c on c.cno = scc.cno
INNER JOIN s stu on stu.sno = scc.sno;
Note that, for this subquery to be useful, you would probably need to select column avg_grade. I would also recommend prefixing each column with the table it belongs to, to remove any possible ambiguity.
Finally: you (probably) can use window functions instead of a subquery:
SELECT stu.sno, sname, cname, scc.
FROM (SELECT *, AVG(grade) OVER() avg_grade FROM sc) scc
INNER JOIN course c on c.cno = scc.cno
INNER JOIN s stu on stu.sno = scc.sno;
Assuming a one-to-many join of students and courses and joined table of student courses (i.e., sc), consider a simplified aggregation on joined tables. Be sure to always qualify columns with alias if query contains more than on table:
SELECT
s.sno AS student_number
, s.sname AS student_name
, c.cname AS course_name
, AVG(sc.grade) AS avg_grade
FROM
sc
INNER JOIN
course c ON c.cno = sc.cno
INNER JOIN
stu s ON s.sno = sc.sno
GROUP BY
s.sno
, s.sname
, c.cname

Sql select clause with three table

I am trying to write a sql query to show student list for each course.
The diagram below show the database relationship.
The SQL query I have written is:
select * from Courses
inner join Enrollments on Enrollments.CourseId = Courses.CourseId
inner join Student on Enrollments.StudentId = StudentId
where Courses.CourseId = 1
The issue is that i am getting returned alot more data than I expected as only one student is registered for the course but i get ten entries. I am not sure if i have done somethings fundamental wrong or is my query the issue.
This is the data
This is the result
I expected only two rows to be returned.
Thanks
Every column in your query must be qualified with the table's name.
You did not qualify the column StudentId in this join:
inner join Student on Enrollments.StudentId = StudentId
If you did you would find the error which is that there is no column StudentId in the table Student and you should use the column Id:
select * from Courses
inner join Enrollments on Enrollments.CourseId = Courses.CourseId
inner join Student on Enrollments.StudentId = Student.Id
where Courses.CourseId = 1
or better with aliases for the tables:
select *
from Courses as c
inner join Enrollments as e on e.CourseId = c.CourseId
inner join Student as s on e.StudentId = s.Id
where c.CourseId = 1
The primary key of table Student is Id, not StudentId.
So the correct query is:
select * from Courses
inner join Enrollments on Enrollments.CourseId = Courses.CourseId
inner join Student on Enrollments.StudentId = Student.Id
where Courses.CourseId = 1

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;

Count() on many to many relationship

I have a Students table and a Language table. They form a many to many relationship using a pivot table Languages_Student.
Is there a way of getting the student which has the biggest amount of languages in common with another student?
I'm not quite sure how to combine COUNT() with some kind of select. This is what I'm working with now:
select * from students student1
inner join languages_student ls1
on student1.id = ls1.student_id
inner join languages l1
on l1.id = ls1.language_id
inner join languages_student ls2
on l1.id = ls2.language_id
inner join students student2
on ls2.student_id = student2.id
where student1.id = 65
group by 16
I'm trying to get the student with biggest amount of languages in common with the student with id 65.
Any ideas?
You can do this using a self-join. Join the junction table to itself on the language column; then aggregate by the student column to get the number in common:
select ls.student_id, count(*) as NumInCommon
from languages_student ls join
languages_student ls65
on ls.language = ls65.language and ls65.student_id = 65 and
ls65.student_id <> ls.student_id
group by ls.student_id
order by count(*) desc;
select ls.student_id, count(ls.language_id) as common
from languages_student ls
where ls.id in (
select l.id
from students s
inner join languages_student ls
on s.id = ls.student_id
inner join languages l
on l.id = ls.language_id
where s.id = 65 )
order by count(ls.language_id)

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