SQL inner joins on three tables - sql

I have three tables that needs to be checked in order to find out on which courses professor is active.
table_teacher
table_course; and
table_teacher_holds_course
table_teacher looks like this:
username | title
---------+----------
john | professor
mark | assistant
table_course looks like this:
course_code | course_name | semester | school_year
-------------+-------------+----------+------------
course_code1| course1 |semester1 | 2015
course_code2| course2 |semester2 | 2015
course_code3| course3 |semester3 | 2015
table_teacher_holds_course looks like this:
username | course_code
---------+-------------
john |course_code1
mark |course_code2
and when I have professors username when he logs on the page, I would like to do left inner join on these three tables in order to show professors courses from table_course
Can someone help me with this, because it is first time to me to use sql to join search in several tables.

Join on the tables common fields
SELECT *
FROM table_teacher t
INNER JOIN table_teacher_holds_course hc ON t.username = hc.username
INNER JOIN table_course c ON hc.course_code = c.course_code
Relevant example:
SELECT t.title, t.username, c.course_code, c.course_name, c.semester, c.school_year
FROM table_teacher t
INNER JOIN table_teacher_holds_course hc ON t.username = hc.username
INNER JOIN table_course c ON hc.course_code = c.course_code
WHERE t.username = 'John'
Result:
title username course_code course_name semester school_year
professor John course_code1 course1 semester1 2015

Related

SQL Query: Customer Table Contains Different type of EmpIDs and i want to get their name in a single row

I got a customer table and each customer has 4 different employeeID (tranieeID,RepresenterID,CoridatorID and ManagerID) I want to get all these employees name rather then their ID in single row.
CustomerTable
|CustomerID|CustomerName|tranieeID|RepresenterID |CoridatorID |ManagerID
------------------------------------------------------------------------
01 Mr T 100 101 102 103
EmployeeTable
EmpID | EmpName
---------------
100 Mr A
101 Mr B
102 Mr C
103 Mr D
What I need
CustomerID | CustomerName | tranieeName | RepresenterName | CoridatorName | ManagerName
----------------------------------------------------------------------------------------
01 Mr T Mr A Mr B Mr C Mr D
I did inner join but I got 4 Rows, is there any way to get all these with a single row?
Thank you for your help!
JOIN should work. I would recommend LEFT JOIN in case any of the values are not filled in:
select c.*,
et.empname as traineeName,
er.empname as RepresenterName,
ec.empname as CoridatorIDName,
em.empname as ManagerName
from customertable c left join
employeetable et
on c.traineeID = et.empid left join
employeetable er
on c.RepresenterID = et.empid left join
employeetable ec
on c.CoridatorID = ec.empid left join
employeetable em
on c.ManagerID = em.empid
You could use a correlated subquery in each instance, such as
select c.CustomerId, CustomerName,
(select Empname from EmployeeTable e where e.EmpID=c.TranieeId) TraineeName,
(select Empname from EmployeeTable e where e.EmpID=c.RepresenterId) RepresenterName,
... etc
from CustomerTable c

Max function returning multiple values [SQL]

I have 3 tables: money, student, faculty. This query returns each faculty and highest stipend in each one of them.
select
f.name as "FACULTY_NAME",
max(stipend) as "MAX_STIPEND"
from
money m, student s
inner join
faculty f on f.id_faculty = s.faculty_id
where
m.student_id = s.id_student
group by
f.id_faculty, f.name;
Query works fine:
FACULTY_NAME | MAX_STIPEND
-----------------+---------------
IT Faculty | 50
Architecture | 60
Journalism | 40
However when I add s.name to original query to also show the name of the student who received max_stipend, query is not working like it used to - it returns all of the students
select
f.name as "FACULTY_NAME",s.name,
max(stipend) as "MAX_STIPEND"
from
money m, student s
inner join
faculty f on f.id_faculty = s.faculty_id
where
m.student_id = s.id_student
group by
f.id_faculty, f.name, s.name;
Query result:
FACULTY_NAME | s.name | MAX_STIPEND
----------------+-----------+---------------
IT Faculty | Joe | 50
IT Faculty | Lisa | 10
Architecture | Bob | 60
Journalism | Fred | 5
Architecture | Susan | 5
Journalism | Tom | 40
It does the same thing using right, left and inner joins. Can someone tell where the problem is?
First, you should be using proper JOIN syntax for all your joins.
Second, you can use Oracle's keep syntax:
select f.name as FACULTY_NAME,
max(stipend) as MAX_STIPEND,
max(s.name) keep (dense_rank first order by stipend desc)
from money m join
student s
on m.student_id = s.id_student join
faculty f
on f.id_faculty = s.faculty_id
group by f.id_faculty, f.name;
However when I add s.name to original query to also show the name of the student who received max_stipend, query is not working like it used to - it returns all of the students
When you add s.name you are looking for min value for each user.
If you want the name of user who has the MAX_STIPEND you should to move to window functions. For example Dense Rank in MS SQL Server.
with cte as
(select
f.name as "FACULTY_NAME",
s.name as "STUDENT_NAME",
stipend as "MAX_STIPEND",
DENSE_RANK() OVER
(PARTITION BY f.name, s.name ORDER BY i.stipend DESC) AS Rank
from
money m
inner join student s on m.student_id = s.id_student
inner join
faculty f on f.id_faculty = s.faculty_id
)
select "FACULTY_NAME", "STUDENT_NAME"
from cte
where rank = 1
Not all sql brands have windowed functions. Here the link for dense_rank on MySQL and also dense_Rank for Oracle

oracle SQL: selecting distinct value where it does not contain another certain value in all its record

This is the current database record that I have, and I only want it to display all the name of students who have never been enrolled in Science.
Name | Subject | Year
-----------------------------------
Ian Lee | Math | 2008
Ian Lee | Science | 2008
Ian Lee | Econs | 2006
Marie-Ann | Geography | 2006
Marie-Ann | Literature | 2009
Natalie S. | Geography | 2006
Julienne | Math | 2008
Julienne | Science | 2008
Julienne | Literature | 2009
Liam | Literature | 2009
Liam | Econs | 2006
I also have a student record Emily Toh that has not been enrolled in any classes yet. But the correct output should be
Name
------------
Marie-Ann
Natalie S.
Emily Toh
Liam
This was what I used to call
SELECT DISTINCT en.Name
FROM ENROLLMENT en
WHERE NOT EXISTS (
SELECT st.Name
FROM STUDENT st
WHERE en.Name = st.Name
AND en.Subject = 'Science'
);
But it still gives me a display of all the student names.
The student table and enrollemnt table is as per:
CREATE TABLE STUDENT(
Name VARCHAR2(50),
DOB DATE,
Address VARCHAR(70),
CONSTRAINT STUDENT_PKEY PRIMARY KEY (Name)
);
CREATE TABLE ENROLLMENT(
Name VARCHAR2(50),
Subject VARCHAR2(70),
Year Number(4),
CONSTRAINT ENROLLMENT_PK PRIMARY KEY (Name, Subject)
CONSTRAINT ENROLLMENT_FKEY FOREIGN KEY (Name) REFERENCES TO STUDENT (Name)
);
The approach you made is right, just start with STUDENTS table, and look for the science enrollment using NOT EXISTS.
SELECT st.Name
FROM STUDENT st
WHERE NOT EXISTS (
SELECT st.Name
FROM enrollment en
WHERE en.Name = st.Name
AND en.Subject = 'Science'
);
One option is to use a subquery to identify all students which you don't want in your result set (i.e. those who were enrolled in Science at some point), and then join to filter off these students.
SELECT s.Name -- , s.Id
FROM STUDENT s
LEFT JOIN
(
SELECT Name -- , Id
FROM ENROLLMENT
WHERE Subject = 'Science'
) t
ON s.Name = t.Name -- AND s.Id = t.Id
WHERE t.Name IS NULL
This approach will capture both students who are enrolled in no classes and students who enrolled, but never enrolled in Science.
Ideally there should also be an Id column associated with each student in both tables. Without this, joining in a meaningful way could be difficult if two or more students were to share the same name.
If you do not need to worry about students who have not enrolled in any classes then you can use GROUP BY ... HAVING ... like this:
SELECT name
FROM enrollment
GROUP BY name
HAVING COUNT( CASE WHEN subject = 'Science' THEN 1 ELSE NULL END ) = 0;
If you have students who have not enrolled for any classes and you want to include them in your output then:
SELECT s.name
FROM student s
LEFT OUTER JOIN
enrollment e
ON ( s.name = e.name AND e.subject = 'Science' )
GROUP BY s.name
HAVING COUNT( e.subject ) = 0;
Since name and subject are the composite primary key for the table then the GROUP BY and HAVING clauses could be replaced with a subject IS NULL check:
SELECT s.name
FROM student s
LEFT OUTER JOIN
enrollment e
ON ( s.name = e.name AND e.subject = 'Science' )
WHERE e.subject IS NULL;

SQL Query - Count() and Inner Join

I want to use an inner join to list the student ID, name, and total number of timetabled hours the student has per week, when the student has more than 4 hours per week.
I have three tables required here, student, studentReg and roomBooking as follows
student
id | fname | surname | courseCode
studentReg
sID | modCode
roomBooking
bookingID | roomCode | moduleCode | dayReq | timeReq | semester | classSize
The SQL query I have so far is
SELECT COUNT(moduleCode) AS [Lecture Hours],
id, fname, surname
FROM (student INNER JOIN studentReg ON student.id = studentReg.sID
INNER JOIN roomBooking ON studentReg.modCode = roomBooking.moduleCode)
HAVING COUNT (moduleCode) > 4;
and when I try to run this, I get "syntax error in expression"
Can anyone help me as to what the problem is?
Never sure with nested join in ms access, but I would try something like that
SELECT COUNT(moduleCode) AS [Lecture Hours],
id, fname, surname
FROM student
INNER JOIN (studentReg
INNER JOIN roomBooking ON studentReg.modCode = roomBooking.moduleCode)
ON student.id = studentReg.sID
GROUP BY id, fname, surname
HAVING COUNT (moduleCode) > 4
or maybe
SELECT COUNT(moduleCode) AS [Lecture Hours],
id, fname, surname
FROM (student INNER JOIN studentReg ON student.id = studentReg.sID)
INNER JOIN roomBooking ON studentReg.modCode = roomBooking.moduleCode
GROUP BY id, fname, surname
HAVING COUNT (moduleCode) > 4;

Joining multiple tables with a single query

Student
student_id FirstName LastName
---------------------------------------------------
1 Joe Bloggs
2 Alan Day
3 David Johnson
Student_Course
course_id student_id courseName
---------------------------------------------------
1 1 Computer Science
2 1 David Beckham Studies
3 1 Geography
1 3 Computer Science
3 3 Geography
Student_clubs
club_id student_id club_name club_count
---------------------------------------------------
1 1 Footbal 10
2 1 Rugby 10
3 1 Syncronized Swimming 10
4 3 Tennis 15
In the above example, student with id = 1 takes 3 course and is part of 3 clubs.
If i was to find out which courses a student is involved in or which club the student is part of i can do it but i will need to run two queries. Is it possible to run a single query against the
tables listed above so that the results come out like this:
Output
student_id FirstName Student_associated_courses Student_associated_clubs
---------------------------------------------------------------------------
1 Joe 1,2,3 Football, Rugby, Syncronized swimming
3 David 1,3 Tennis
Is it possible to get the above output with just one query? I am using JDBC to get the data so i am trying to see if i can avoid multiple trips to get the necessary data.
use GROUP_CONCAT with DISTINCT in MySQL
SELECT a.student_ID, a.firstname,
GROUP_CONCAT(DISTINCT b.course_ID),
GROUP_CONCAT(DISTINCT c.club_name)
FROM student a
INNER JOIN student_Course b
ON a.student_id = b.student_ID
INNER JOIN student_clubs c
ON a.student_ID = c.student_ID
GROUP BY a.student_ID, a.firstname
See SQLFiddle Demo
Try it like this:
SELECT *
FROM Student s JOIN
(SELECT sc."student_id", listagg(sc."course_id", ',')within group(ORDER BY sc."course_id")
FROM Student_Course sc
GROUP BY sc."student_id") s_course ON s."student_id"=s_course."student_id"
JOIN (SELECT sl."student_id", listagg(sl."club_name", ',')within GROUP(ORDER BY sl."club_name")
FROM Student_clubs sl
GROUP BY sl."student_id") s_club ON s."student_id"=s_club."student_id"
The "catch" is that LISTAGG doesn't work with DISTINCT keyword
Here is a fiddle