Relational Algebra and Confusing one Complex Examples - sql

We have 3-Relation:
Students(sid, sname)
Courses(cid, cname, dept)
take(sid, cid, grade)
We want to find student numbers of students whose these students take all courses that present in 'CS' department.
why (line 4) is the answer of this query ?
anyone could say differ from (line 1) to (line 3). I read this in Solved-Ex in DB.

Another way to phrase the fourth line is:
all students
except
(
all combinations of students and CS courses
except
all taken courses
)
The set between brackets contains all student + CS course combinations that were not taken. Subtract this from all students, and you get the students that did take all CS exams.

Related

SQL question: how to find rows that share all of the same rows in a composite table?

I'm working on my SQL project using the Oracle database for class, and I'm asked a question that I see far too often.
You have three tables:
STUDENT: SNO, SNAME
CLASS: CNO, CNAME
ATTENDANCE: SNO, CNO, Grade
The question I keep finding is of a similar type: Find the names of the students that attend in all of the classes that "John" (or anyone else) attends.
John attends three classes, so I have to find the students that also attend those three classes (could be more, but those three must be there). However, I won't always know how many classes John (or whoever) attends, so it can't be hardcoded like that.
SELECT jclass.CNO
FROM attendance jclass
INNER JOIN student on jclass.SNO = student.SNO
WHERE student.SNAME = 'John';
This gets me the classes that John attends. I tried to add the identifier for the other students:
SELECT student.SNAME
FROM student
INNER JOIN attendance on student.SNO = attendance.SNO
INNER JOIN class on attendance.CNO = class.CNO
WHERE student.SNAME <> 'John'
AND class.CNO IN (SELECT jclass.CNO
FROM attendance jclass
INNER JOIN student on jclass.SNO = student.SNO
WHERE student.SNAME = 'John');
However, this only gets me the students that appear in at least one of John's classes, rather than all of them. I can see why it's doing this, but I'm not sure how to fix it. It's the one big struggle I'm having with SQL.
Here is one way - assuming SNO is primary key in the first table, CNO is primary key in the second table, and (SNO, CNO) is (composite) primary key in the third table, and that the input student is given by a unique identifier (first name is distinctly NOT a unique identifier, so the problem stated in terms of giving "John" as the input makes no sense). Here I assume the "special" student is identified by SNO = 1001; you can make 1001 into a variable, or change it to a subquery that selects a (unique!!) SNO based on some other inputs.
I didn't try to make the query as efficient as possible, or use features you most likely haven't seen in your class. Rather, I tried to make it as elementary and as readable as possible.
select sno
from attendance
where cno in (select cno from attendance where sno = 1001)
group by sno
having count(*) = (select count(*) from attendance where sno = 1001)
;
The strategy is simple: the subquery in the in condition finds the classes attended by the "special" student, then from the attendance table we select only rows for those classes. Group by student, and count. Keep only the students for whom the count is equal to the total count for the "special" student. Note the last condition is about groups, not about input rows, so it belongs in the having clause.

sql query to count and display full name

So I am very confused by this query to display teachers' full name. I have used teacher_name ||' '|| teacher_lastname, but when I try to do so for the students, something happens. I am trying to get number of times a teacher had an appointment with a student and display teacher fullname and students full name. Below is my query :
select d.teacher_id ||' '|| d.teacher_lastname as Teacher, count (distinct c.student_id)
from teacher d inner join class t on t.teacher_id=d.teacher_id
inner join classRoom tp on tp.dest_id=t.dest_id
inner join classFl ta on tp.dest_id=ta.dest_id
inner join students c on c.student_id=ta.student_id
group by d.teacher_id,d.teacher_lastname
This is the output, but when I add
select d.teacher_id ||' '|| d.teacher_lastname as Teacher,
c.student_name||' '||c.student_lastname as Student,
count (distinct c.student_id)
and also add it to the group by d.teacher_id,d.teacher_lastname,c.customer_name,c.customer_lastname
it gives the following result
What is wrong? How can I fix it to have the same nr of counts for each teacher?
I am using oracle sql developer
I think you need to remove the DISTINCT from inside the COUNT. You say you want "nr of times a teacher had an appointment with a student" which I interpret to mean that if teacher T saw student S 9 times you want:
T S 9
If you keep DISTINCT, then grouping on student name and counting the number of unique student IDs will only ever produce a 1 unless two students have identical names. In essence then, by keeping the DISTINCT you are counting "the number of different students with name X that that teacher met with" and mostly this is 1 because "1 unique student named Hana Baker met with xxxx yyyy", "1 unique student with name Dream Kenise met with xxxx yyyy" ....
If you do have students with the same name but a different ID, then you should add student ID to the GROUP BY to provide distinction between the two students. You don't have to add it to the SELECT, but you'll struggle to tell them apart if you do. If you have two students both called S, but one has ID 1 (and he saw T 5 times) and the other has ID 2 (and she saw T 4 times) you'll get a result of:
T S 5
T S 4
You might want to add other columns to your select to better tell the difference between them
In your first query, using DISTINCT meant "the number of different students that sought a meeting with the teacher". Omitting DISTINCT in that query would have counted "the number of times the teacher had a meeting with any student"

'for all' Queries in sql

In class, professor said that SQL language does not provide 'for all' operator.
In order to use 'for all' you have to use 'not exist( X except Y)'
At this point, I can't figure out why 'for all' is same meaning as 'not exist( X except Y)'
I give you example relation:
course (cID,title,deptName,credit),
teaches (pID,cID,semester,year,classroom),
student (sID,name,gender,deptName)
Q: Find all student names who have taken all courses offered in 'CS' department
The answer is:
Select distinct
S.sid, S.name
from
student as S
where
not exists (
(select cID from course where deptName = 'CS')
except
(select T.cID from takes as T where S.sID = T.sID)
);
Can you give me specific explain about that?
ps. Sorry for my English skill
You professor is right. SQL has no direct way to query all records that have all possible relations of a certain type.
It's easy to query which relations of a certain type a record has. Just INNER JOIN the two tables and you are done.
But in an M:N relationship like "students" to "taken courses" it's not that simple.
To answer the question "which student has taken all possible courses" you must find out which relations could possibly exist and then make sure that all of them do actually exist.
select distinct
S.sid, S.name
from
student as S
where
not exists (
(select cID from course where deptName = 'CS')
except
(select T.cID from takes as T where S.sID = T.sID)
);
can be translated as
give me all students SELECT
for whom it is true: WHERE
that the following set is empty NOT EXISTS
(any course in 'CS') "all relations that can possibly exist"
minus EXCEPT
(all courses the student has taken) "the ones that do actually exist"
In other words: Of all possible relations there is no relation that does not exist.
There are other ways of expressing the same thought that can be used in database systems without support for EXCEPT.
For example
select
S.sid,
S.name
from
student as S
inner join takes as T on T.sID = S.sID
inner join course as C on C. cID = T. cID
where
c. deptName = 'CS'
group by
S.sid,
S.name
having
count(*) = (select count(*) from course where deptName = 'CS');
From your table definition and requirement its not clear what is the use of teaches table. You want the list of students names those have taken all courses offered by 'CS' department. For this students and course table is enough.
SELECT name
FROM
(
SELECT B.name, A.cid
FROM course A
INNER JOIN student B ON A.deptName = B.deptName
WHERE A.deptName = 'CS'
GROUP BY A.cid, B.name
) A
GROUP BY name
HAVING COUNT(name) >= (SELECT COUNT(cid) FROM course WHERE deptName = 'CS')
Internal query just selects all students those have taken any course offered by 'CS' dept and with group by I just make sure that in case a student take same course twice they will be counted as one row. Next I just select those students take all course offered by 'CS' dept.
I think you have some gap to understand your requirement properly. In your requirement no relation with teaches table is specified.
Q: Find all student names who have taken all courses offered in 'CS'
department
NOT EXISTS returns true if the query passed to it contains 0 records.
In this case, your sub-query from NOT EXISTS selects all the courses offered in 'CS', and subtract from this result set all the courses taken by specific student.
If the student have taken all the courses then except will remove all and the sub-query will return 0 records, which in pair with NOT EXISTS will give you true for specific student, and it will be displayed in final result set.
Brief history: Codd invented the Relational Model (RM), some people created a DBMS loosely based on RM to prove a RM product could be performant, and the SQL language emerged based on that DBMS (i.e. not directly based on the RM).
Codd came up with a set of primitive operators to define a database as being relationally complete. His algebra included product, where two relations are 'multiplied' together to give a combined relation; this made it into SQL as CROSS JOIN. [Side note: people refer to this operator as 'Cartesian product', which results in a set of ordered pairs. However, product in RM results in a relation (as do all relational operators), and CROSS JOIN results in a table expression (loosely speaking).]
Codd's algebra also included a division operator. I guess the thinking is, we should be able to take the result of product and one of the relations and use an operator to result in the other relation. But it does has some practical use too, of course. It is commonly expressed as 'the supplier who supplies all products', after Chris Date's parts and suppliers database found in his books. SQL lacks an explicit division operator, so we need to use other operators to get the desired result.
Note there are two flavours of division, being exact division ("suppliers who supply all the parts we are interested in and no more") and division with remainder ("suppliers who supply at least all the parts we are interested in and possibly more"). I tend to be wary of the answers here that do not mention either the name 'division' or that you need to decide whether you need to deal with remainders.
The thinking behind your professor's answer is that a double negative (in mathematics and English) i.e. if the statement "there is no part I don't supply" is true for a given supplier then that supplier will be in the result.
Note there are operators that Codd omitted (e.g. rename and summerize) that can now be found in SQL, so it's a shame we are still waiting for division!

SQL, how to have the lowest average?

I have the following database
Student (NumStu, Name, Surname, BirthDate, Street, PC, City)
Teach (CodeTeach, Label, Coef)
Exam (numexam, DateExam, Place, #CodeTeach)
Notation (#NumStu, #NumTeach, Note)
And I want to have the list of the students who have the lowest average in the teach of Computer Science.
How to do it?
Do you need to select all students sorted by teach in Computer Science or only 1 student with lowest grade? You should add more description.
If students are inserted in Student table and their teaching grades in table Teach should be something like that(I can help you correctly after you give more detailed description):
SELECT S.NumStu, S.Name, S.Surname, S.BirthDate, S.Street, S.PC, S.City, T.ComputerScience
FROM Student AS S
INNER JOIN Teach AS T
ON T.AssociatedColumn = S.AssociatedColumn
ORDER BY T.ComputerScience -- Here you order students list from lowest grade (ascending)
SELECT *
FROM Student, Notation
ON Student.NumStu = Notation.NumStu
WHERE Notation.Note =
(
SELECT MIN(AVG(Note))
FROM Notation
GROUP BY NumStu
)
Explanation: This request will show you everything from the tables Student and Notation, when Note will be equal to the Minimum of the Average of Note.
I am not certain it will work, waiting for feedback.
P.-S: Am french too

SQL Server query to know if an id in a column equals to other in another column

I’ve been hours trying to build this query and I need your help so I can make it.
This is table Students (made out of inner joins):
SpecialtyChosenID StudentID Subject SubjectSpecialtyID
5ABFB416-8137 15 Math A1EBF3CB-E899
5ABFB416-8137 15 English A1EBF3CB-E899
The info in it means that a student with id no. 15 has chosen an specialty with id 5ABFB416-8137
The two subjects he has passed (Math and English) belong to a specialty with id A1EBF3CB-E899
What would be the query to know if the passed subjects belong to the specialty chosen by the student??
Counting the number of subjects with the same SubjectSpecialtyID as SpecialtyChosenID and vice versa could do.
Thanks a lot
You can do a self join. This finds the number of subjects taken by the student that match the student's chosen specialities.
SELECT l.SpecialtyChosenID, l.StudentID, Count(Distinct r.Subject) FROM Students l
LEFT JOIN Students r ON (l.StudentID=r.StudentID AND l.SpecialityChosenID=r.SubjectSpecialityID)
GROUP BY l.SpecialtyChosenID, l.StudentID
However, this is quite inefficient using the table structure given. If you have a table listing students with their specialities, and another with subjects and specialities, and a third relating students with subjects, it would be better to build this query from the base data, rather than from the derived data.
SELECT * FROM Students WHERE SpecialtyChosenID = SubjectSpecialtyID
If you only need the list of matching subjects and you have the SpecialtyChosenID you can do something like
SELECT * FROM Students
WHERE SubjectSpecialtyID = SpecialityChosenID
CASE WHEN SpecialtyChosenID = SubjectSpecialtyID THEN 1 ELSE 0 END AS specialty