In PostgreSQL how do you join two tables select separate information - sql

Having trouble with joins.
I have a table called subjects
subno subname
30006 Math
31445 Science
31567 Business
I also have a another table called enrollment
subno sno
30009 980008
4134 988880
etc..
how to list subject numbers and subject names for student 9800007 ?

If you want to return zero rows for students without an enrolment, use a LEFT [OUTER] JOIN, eg:
SELECT e.sno, s.subno, s.subname
FROM enrollment e LEFT OUTER JOIN subjects s ON s.subno = e.subno
WHERE e.sno=988880;
To return no rows for students without enrolments, use an INNER JOIN:
SELECT e.sno, s.subno, s.subname
FROM enrollment e INNER JOIN subjects s ON s.subno = e.subno
WHERE e.sno=988880;
Note that join order is important for outer joins (RIGHT [OUTER] JOIN and LEFT [OUTER] JOIN - the OUTER keyword is optional) but not for INNER JOIN. For that reason, #swetha's answer has a problem: the join order is reversed if you're looking for information about a student.
See this SQLFiddle

Try this
select *
from subjects s
left join enrollment e on s.subno = e.subno
where sno=9800007

Related

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

multi inner join in where exists invalid identifier

I have the below query with multi joins you can find it on sql fiddle thats giving me invalid DEF.ID
How can I make the below join to read from DEFINITION table
SELECT *
FROM DEFINITION DEF
WHERE EXISTS (SELECT 1
FROM EMPLOYEE E
INNER JOIN MONTHLY_PAYMENT MP ON (MP.ID = E.CODE)
INNER JOIN DEPARTMENT DEP ON (DEP.ID = DEF.ID)
)
By Oracles rules, there are two problems, first DEPARTMENT needs to be related to at least one of the the two tables it is being joined to. And second, DEF cannot be used as a nested inner condition join and so must be used in the WHERE clause instead.
SELECT DEF.ID
FROM DEFINITION DEF
WHERE EXISTS
(
SELECT 1 FROM EMPLOYEE E
INNER JOIN MONTHLY_PAYMENT MP ON (MP.ID=E.CODE)
INNER JOIN DEPARTMENT DEP ON DEP.ID=E.DEP_ID
WHERE DEP.ID=DEF.ID
)

How can i apply left outer join conditions on four tables?

i was trying to apply joins on 4 tables. but i could not find proper result for that.
i have 4 tables like 1.students,2.college,3.locations,4.departments. so i have same column sid in all tables which can be used to join conditions.
i want all matched rows from four tables as mentioned columns in select statement below and unmatched rows in left table which is left outer join work.
i have tried this syntax.
select
students.sname,
college.cname,
locations.loc,
department.dept
from students, college, locaions, departments
where student.sid=college.sid(+)
and college.sid=locations.sid(+)
and locations.sid=department.sid(+);
is this right ?
This is an old-fashioned way of outer-joining in an Oracle database. For Oracle this statement is correct; in other DBMS it is invalid.
Anyway, nowadays (as of Oracle 9i; in other DBMS much longer) you should use standard SQL joins instead.
select
s.sname,
c.cname,
l.loc,
d.dept
from students s
left outer join college c on c.sid = s.sid
left outer join locations l on l.sid = c.sid
left outer join departments d on d.sid = l.sid;
Given what you've shown it would appear that what you really want is
select s.sname,
c.cname,
l.loc,
d.dept
from students s
LEFT OUTER JOIN college c
ON c.SID = s.SID
LEFT OUTER JOIN locations l
ON l.SID = s.SID
LEFT OUTER JOIN departments d
ON d.SID = s.SID
The issue in your original query is that because an OUTER JOIN is an optional join, you can end up with NULL values being returned in one of the join fields, which then prevents any of the downstream values being joined. I agree with #ThorstenKettner who observes in a comment that SID is apparently a "student ID", but it's not reasonable or appropriate to have a "student ID" field on tables named COLLEGE, LOCATIONS, or DEPARTMENTS. Perhaps you need to update your design to allow any number of students to be associated with one of these entities, perhaps using a "join" table.
Best of luck.

How to combine natural join and left outer join in SQL

I have 3 tables students, enrollments, and classes. I want to list every student and the department code of each class they have taken (department code is in classes) including the students who have not taken any classes (department code would be blank in this case). This is necessary because of future computations that will be done.
Here is how I've thought about this. The following query does a left join of students and enrollments so that I include students who have not enrolled in any classes
select s.B#, s.firstname, e.classid
from students s, enrollments e
where s.B#=e.B#(+)
This query works as expected.
Next, I join enrollments and classes so that I can see the corresponding dept_code for each enrollment:
select B#, dept_code
from enrollments natural join classes
This is also working as expected.
When I try to bring the two together though I run into problems
select s.B#, s.firstname, dept_code
from students s, (enrollments e natural join classes)
where s.B# = e.B#(+)
Attempting to run this query I get the error:
ORA-25156: old style outer join (+) cannot be used with ANSI joins
Can anyone please explain what's going on here?
You will be better off using explicit JOIN instead of old-style comma-separated joins with where clause. Same goes here for (+).
Write your query like that instead:
select s.B#, s.firstname, dept_code
from students s
left join (
select * -- if there are same names of columns in both tables, specify them explicitly
from enrollments e
join classes c on e.? = c.? -- specify here your natural join columns
) e on s.B# = e.B#
Btw, the error tells you exactly that you can't combine old style joins with ANSI joins. Probably time to switch to the newer format which exists for a long time now.
the brackets are not really needed, I put them there just to emphasize the structure
select s.B#, s.firstname, c.dept_code
from students s
left join ( enrollments e
join classes c
on c.classId = e.classId
)
on s.B# = e.B3
;
Might as well be written like this (only a different format)
select s.B#, s.firstname, c.dept_code
from students s
left join enrollments e
join classes c
on c.classId = e.classId
on s.B# = e.B3
;
P.s.
If you have doubt about this syntax, you might want to try this:
create table t1 (i int);
create table t2 (i int);
create table t3 (i int);
create table t4 (i int);
create table t5 (i int);
insert into t1 (i) values (1);
insert into t2 (i) values (1);
insert into t3 (i) values (1);
insert into t4 (i) values (1);
insert into t5 (i) values (1);
select *
from t1
join t2
join t3
join t4
join t5
on t5.i=t4.i
on t4.i=t3.i
on t3.i=t2.i
on t2.i=t1.i
;
Your query should look something like this:
select s.B#, s.firstname, c.dept_code
from students s left join
enrollments e
on s.B# = e.B3 left join
classes c
on e.classId = b.classId; -- I am guessing what the `JOIN` column is
In other words, you need two left joins to keep all the students. Otherwise, the on clause might filter rows out.
I also strongly encourage you to avoid and un-learn NATURAL JOIN. It is just a bug waiting to happen in code. It matches columns only based on their names -- not even taking into account declared foreign key relationships.
It hides the columns being joins, which makes debugging and understanding queries much more difficult. For instance, because tables I create almost always have columns such as CreatedAt and CreatedBy, it can't be used.
EDIT: (For lgrade)
You would put the condition in the ON clause:
select s.B#, s.firstname, c.dept_code
from students s left join
enrollments e
on s.B# = e.B3 and e.lgrade is not null left join
classes c
on e.classId = b.classId; -- I am guessing what the `JOIN` column is
I strongly encourage you to avoid and un-learn outer join because it produces nulls. Having nulls is just a bug waiting to happen in code. Outer join isn't really a join at all, rather a 'unnatural' union, using nulls to force things together.
Suggested alternative approach: do the union yourself using an appropriate default value:
select B#, firstname, dept_code
from students
natural join enrollments
natural join classes
union
select B#, firstname, '{{NONE}}' as dept_code
from students
where B# not in ( select B# from enrollments );

Adding filter on the right side table on Left outer joins

On outer joins(lets take a left outer join in this case) how does adding a filter on the right side table work?
SELECT s.id, i.name FROM Student s
LEFT OUTER JOIN Student_Instructor i
ON s.student_id=i.student_id
AND i.name='John'
I understand that if the filter was on the Student table it would be more like "Get all rows with name= John first and join the tables".
But I am not sure if that is the case if the filter is on the right side table(Student_Instructor). How does the filter i.name='John' gets interpreted?
Thank you
Should be the same as:
SELECT s.id FROM Student s
LEFT OUTER JOIN (Select * from Student_Instructor where name='John' ) i
ON s.student_id=i.student_id
In your example query, only rows where i.name = 'John' would be returned. I think you would want to also include or i.name is null to include all rows where a Student record does include a student Instructor.
SELECT s.id FROM Student s , isnull(i.name, 'No Student Insturctor') as [Student Instructor]
LEFT OUTER JOIN Student_Instructor i
ON s.student_id=i.student_id
AND i.name='John' or i.name is null
All rows will be returned from your left table regardless. In the case of a left join, if the filter isn't met, all data returned from the right table will show up as null. In your case, all students will show up in your results. If the student doesn't have an instructor, i.name will be null.
Since you are only selecting a column from your left table, your join is pretty useless. I would also add i.name to your select, so you can see the results
In the case of an inner join, rows will only be returned if the join filter is met.
This can be done using Oracle (+) notation as well-
SELECT s.id FROM Student s, Student_Instructor i
WHERE s.student_id = i.student_id (+)
AND i.name(+)='John'
Although, Oracle recommends that you use the FROM clause OUTER JOIN syntax rather than the Oracle join operator.