SQL: Names of courses that Mike can take next semester - sql

Mike can take a CS course X only if he has not successfully finished X, or he has successfully finished all the courses on which course X is built. For example, Mike cannot take IE2900, because he has not successfully finished IE2700.
(In the query, only the courses on which a course is directly built should be considered. For example, IE3200 is only directly built on IE2201, not on IE1100).
In short, find a query for codes of CS courses that Mike can take next semester.
The result should be like the following:
Code
----
IE2700
IE3200
Below are the database tables with example data:
Students
Sid Name
---------- ----------
2 Kathlyn
6 Mike
7 Nick
9 Anny
Courses
Code Title Dept Credits
---------- ----------- ---------- ----------
IE1100 programming CS 10
IE2201 operating s CS 20
IE2700 database CS 10
IE2900 software en CS 10
IE3200 distributed CS 10
MAT1001 calculus Math 30
DOK1012 database Hum 10
CoursesBuiltOn
Code BuiltOn
---------- ----------
IE2201 IE1100
IE2700 IE1100
IE2900 IE2201
IE2900 IE2700
IE3200 IE2201
Grades
Sid Code Year Grade
---------- ---------- ---------- ----------
2 MAT1001 2010 A
2 IE1100 2011 A
2 IE2201 2012 A
2 IE2700 2013 A
2 IE2900 2014 B
2 IE3200 2014 B
6 MAT1001 2011 D
6 IE1100 2012 C
6 IE2201 2013 B
6 IE2700 2013 F
7 MAT1001 2013 B
7 DOK1012 2013 A
9 MAT1001 2013 F
The database structure:
CREATE TABLE Students (
Sid TEXT PRIMARY KEY,
Name TEXT
);
CREATE TABLE Courses (
Code TEXT PRIMARY KEY,
Title TEXT,
Dept TEXT,
Credits INT
);
CREATE TABLE Grades (
Sid INT,
Code TEXT,
Year INT,
Grade TEXT,
PRIMARY KEY (Sid, Code),
FOREIGN KEY (Sid) REFERENCES Students (Sid),
FOREIGN KEY (Code) REFERENCES Courses (Code)
);
CREATE TABLE CoursesBuiltOn (
Code TEXT,
BuiltOn TEXT,
PRIMARY KEY (Code, BuiltOn),
FOREIGN KEY (Code) REFERENCES Courses (Code),
FOREIGN KEY (Code) REFERENCES Courses (BuiltOn)
);
I tried to join all the tables together on their foreign keys and get out information, but I could not find any way to get the right answer.
EDIT:
This is what I got so far:
with NotAvailable as (select COB.Code
from Courses C
left join CoursesBuiltOn COB on COB.BuiltOn = C.Code
left join Grades Gr on Gr.Code = C.Code
inner join Students S on S.Sid = Gr.Sid
where Gr.Sid = S.Sid and Gr.Grade = 'F'
), AlreadyTakenAndPassed as (select Gr.Code
from Grades Gr where Gr.Sid = S.Sid and Gr.Grade <> 'F'
), TakenButNotYetPassed as (select Gr.Code
from Grades Gr where Gr.Sid = S.Sid and Gr.Grade = 'F'
)
select COB.Code
from Courses C
left join CoursesBuiltOn COB on COB.BuiltOn = C.Code
left join Grades G on G.Code = C.Code
inner join Students S on G.Sid = S.Sid
where C.Dept = 'CS' and S.Name = 'Mike'
and (COB.Code in TakenButNotYetPassed or COB.Code not in NotAvailable or COB.Code not in AlreadyTakenAndPassed)
;
Code
----------
IE2700
IE2900
IE3200
IE2900
select COB.Code
from Courses C
left join CoursesBuiltOn COB on COB.BuiltOn = C.Code
left join Grades G on G.Code = C.Code
inner join Students S on S.Sid = G.Sid
where S.Name = 'Mike' and G.Grade = 'F';
Code
----------
IE2900
As you can see in the query above, I wanted to make subqueries that will exclude courses from the whole set of the courses. However, the NotAvailable subquery will not exclude IE2900. Why is that? I thought I was on the right way... this might not be the most efficient approach touch...

I did it myself, by cutting the total query into cases:
with
TakenButNotYetPassed as (select Gr.Code
from Grades Gr
inner join Students St on St.Sid = Gr.Sid
where St.Name = 'Mike' and Gr.Grade = 'F'
),
NotAvailable as (select Code
from CoursesBuiltOn
where BuiltOn in TakenButNotYetPassed
),
Passed as (select Gr.Code
from Grades Gr
inner join Students St on St.Sid = Gr.Sid
where St.Name = 'Mike' and Gr.Grade <> 'F'
)
select *
from Courses C
where C.Dept = 'CS'
and C.Code not in Passed
and C.Code not in NotAvailable
;

Related

Retrieving dissimilar data using WHERE NOT in the joins?

I need to retrieve the name of those departments in which no students are enrolled.
Table: department
dept_id dept_name
1 IT
2 Electrical
3 Civil
4 Mechanical
5 Chemical
Table: stud_member
f_name dept_id age
AB 2 19
Rose 3 22
May 1 20
Noor 1 21
Haya 1 19
April 3 23
Sakina 2 20
For example the names of mechanical and chemical. I have written this query for it using outer join (explicitly maybe?) But is shows an error.
please tell me that why i cannot write:
SELECT dept_id, dept_name
FROM department
LEFT JOIN stud_member ON (WHERE NOT department.dept_id = stud_member.dept_id);
I will be grateful if anyone will tell me the correct answer!
Assuming dept_id in stud_member can not be NULL which is true when dept_id is a FOREIGN KEY
SELECT dept_id, dept_name
FROM department
WHERE dept_id NOT IN (SELECT dept_id FROM stud_member);
as suggested using NOT EXISTS does not have this problem
SELECT d.dept_id, d.dept_name
FROM department d
WHERE NOT EXISTS (SELECT * FROM stud_member s WHERE d.dept_id = s.dept_id);
You can select all departments where Right part of Left JOIN is NULL
SELECT d.dept_id, d.dept_name
FROM department d
LEFT JOIN stud_member sm ON d.dept_id = sm.dept_id
WHERE sm.dept_id IS NULL

SQL inner joins on three tables

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

Show Values of single rows into multiple columns

I need to create sql that will list pairs who have the same plant
There are 3 tables
Plant_Table
--------------
Snum Plant cost
A Rose 5.00
B Willow 6.00
C Lilly 7.00
Landscaper_Table
----------------
Lnum Lname Laddress
1 Dr Plant xxx st
2 Plant Scaper zzz st
3 George Gardener yyy st
land_plant_Table
---------------
Lnum Snum instock
1 A 3
2 A 3
2 B 3
2 C 3
3 A 3
3 C 3
I need to list pairs of contractors, and the name of the supplies for contractors who keep the same supplies in stock
An example of results I want would be
Landscaper1 Landscaper2 Landscaper 3 Plant
Dr plant George Gardener Plant Scaper Rose
Plant Scaper George Gardener --- Lilly
How can i do this ?
Since you have not mentioned the database, I've solved it using sql server.
select plant,
max(case when row='1' then name end) L1,
max(case when row='2' then name end) L2,
max(case when row='3' then name end) L3
from
(
select lt.lname as name, pt.plant as plant,
row_number() over(partition by pt.plant order by pt.plant) as row
from plant_table pt
inner join land_plant_Table lpt on lpt. snum = pt.snum
inner join Landscaper_Table lt on lpt.lnum = lt.lnum
) as s
group by plant
fiddle
As far as i know, its not possible to show in pairs...a single file printing in two columns is suggeted by below answer
Simply join land_plant_Table with Plant_Table and Landscaper_Table based on Lnum and Lnum and filter out using supplies in where clause.
Something like this would do ( based on your last table schema before update! )
via joins
select lt.lname, pt.plant
from plant_table pt
inner join land_plant_Table lpt on lpt. snum = ct.snum
inner join Landscaper_Table lt on lpt.lnum = lt.lnum
where pt.plant='rose' /* or pt.plant IN ('rose','willow','blah','blah','blah' )*/
via subquery :
1st fetch the the snum -> then based on snum, fetch lnum -> based on lnum, fetch lname
select lname from Landscaper_table where lnum in (
select lnum from land_plant_table where lnum in (
select snum from plant_table where plant IN ('rose','willow','blah','blah','blah' )
)

not displaying 'none' instead of a 0

Right now my query is giving me a 0 when I count the enrolled date. How could I have a row show me 'none' result instead of a 0.
what I am missing here ?
COALESCE(TO_CHAR(SUM(ENROLLMENTS)),'none') ENROLLMENTS
this is what I have
SELECT lt.STUDENT_ID,lt.FIRST_NAME, lt.LAST_NAME,COALESCE(TO_CHAR(SUM(ENROLLMENTS)),'none') ENROLLMENTS
FROM STUDENT lt
LEFT OUTER JOIN
(SELECT s.STUDENT_ID, z.COURSE_NO,COUNT(e.ENROLL_DATE) AS ENROLLMENTS
FROM STUDENT s
LEFT JOIN ENROLLMENT e ON s.STUDENT_ID = e.STUDENT_ID
LEFT JOIN SECTION z ON e.SECTION_ID = z.SECTION_ID
WHERE s.PHONE LIKE '702%'
GROUP BY s.STUDENT_ID, z.COURSE_NO) rt
ON lt.STUDENT_ID = rt.STUDENT_ID
WHERE lt.PHONE LIKE '702%'
GROUP BY lt.STUDENT_ID,lt.FIRST_NAME, lt.LAST_NAME,ENROLLMENTS
ORDER BY lt.LAST_NAME,lt.FIRST_NAME;
instead of having results
STUDENT_ID FIRST_NAME LAST_NAME ENROLLED
---------- ------------------------- ------------------------- -----------
253 Walter Boremmann 1
396 James E. Norman 0
etc
I'd like to have it like this
STUDENT_ID FIRST_NAME LAST_NAME ENROLLED
---------- ------------------------- ------------------------- -----------
253 Walter Boremmann 1
396 James E. Norman none
etc
I think you need a CASE statement because TO_CHAR is returning "0":
(CASE WHEN SUM(ENROLLMENTS)=0 THEN 'none' ELSE SUM(ENROLLMENTS) END) ENROLLMENTS

How to create a relationship between records in a SQL database?

Let's say we have a "People" table:
ID Name Surname Age
1 Thorvald Benjamin 32
2 Addison Impi 40
And we have a "Compatibility" table:
Person1_ID Compatibility Person2_ID
1 89 2
How can you create a relationship between the value of Person1_ID and a record in the People table? And also the same thing in Person2_ID?
And when you are fetching data from the database, what would be a good way to write the SQL query so that you can get the information about the Person1, the Person2, and their compatibility?
I don't know much about databases, and this is probably an extremely easy question. Please bear with me.
Something like that :
SELECT
A.Person1_ID, A.Compatibility, A.Person2_ID,
B.Name AS Person1_name, B.Surname AS Person1_surname, B.Age AS Person1_age,
C.Name AS Person2_name, C.Surname AS Person2_surname, C.Age AS Person2_age
FROM table_compatibility as A
LEFT JOIN table_people AS B
ON A.person1_ID = B.ID
LEFT JOIN table_people AS C
ON A.person2_ID = C.ID
WHERE A.Person1_ID = 1
This is a working example.
drop table person_example cascade constraints;
drop table compatibility_example cascade constraints;
create table person_example (person_id number primary key, name varchar2(50),age number);
create table compatibility_example (compat_id number,person_id1 number, person_id2 number);
alter table compatibility_example add
(
constraint fk_comp_per1 foreign key (person_id1) references person_example(person_id),
constraint fk_comp_per2 foreign key (person_id2) references person_example(person_id)
);
insert into person_example (person_id,name,age) values(1,'John',23);
insert into person_example (person_id,name,age) values(2,'Josh',24);
select * from person_example;
PERSON_ID NAME AGE
---------- -------------------------------------------------- ----------
1 John 23
2 Josh 24
2 rows selected.
insert into compatibility_example (compat_id,person_id1,person_id2 ) values(1,1,2);
select * from compatibility_example;
COMPAT_ID PERSON_ID1 PERSON_ID2
---------- ---------- ----------
1 1 2
1 row selected.
set linesize 750
select compat_id,person_id1,person_id2,
p1.name person_1, p1.age age1, p2.name person_2, p2.age age2
from
compatibility_example ce
left outer join person_example p1
on ce. person_id1=p1.person_id
left outer join person_example p2
on ce. person_id2=p2.person_id;
COMPAT_ID PERSON_ID1 PERSON_ID2 PERSON_1 AGE1 PERSON_2 AGE2
---------- ---------- ---------- -------------------------------------------------- ---------- -------------------------------------------------- ----------
1 1 2 John 23 Josh 24
1 row selected.