简体   繁体   中英

sql division using not exist

I am trying to wrap my head to understand the working of division using NOT EXISTS. I know how to write SQL query using not Exist.But somehow, i am missing how the evaluation is happening for such queries. Any explanation and help will be greatly appreciated.

My query is : Give the name of students who are enrolled in all the courses.

student table

SID SName
S1 Amy
S2 Tracy

course table

CID coursename
C1  computer
C2  Biology

enrolled table. Key is SID and CID

SID  CID
S1   C1
S1   C2
S2   C1

My SQL query is as below:

SELECT s.SName
FROM student s
WHERE NOT EXISTS
(SELECT CID from course c
WHERE NOT EXISTS
(SELECT S.SID 
FROM enrolled E
WHERE S.SID=E.SID AND
C.CID=E.CID));

My understanding is:

For S1: inner query will fetch - C1 first and check if S1 is enrolled in C1 or not. In our case. he is. it will return false - then it will check for C2 and again it is false.

is it so? i am confused now.

The SQL appears to be OK.

To answer your actual question : "But somehow, i am missing how the evaluation is happening for such queries".

What happens conceptually is that the outermost select [FROM STUDENT AS S] ranges over all the students one by one, each time retaining the attribute values from the "current" row in context for further reference.

Then the outermost WHERE NOT EXISTS goes scanning all the courses [SELECT FROM COURSE] one by one, causing once again the attribute values from the "current" row to be retained in context for further reference [so now we have both a "current" student row as well as a "current" course row].

Then the innermost WHERE NOT EXISTS goes checking whether there is an enrollment for the student from context and the course from context. If there isn't, then there is a course that the student is not enrolled on so that particular student does not follow all courses.

I've stressed conceptually and I'll stress it again, because the actual data access strategies used in the computation may be totally different . But the results must be as if this process was carried out as described.

EDIT

In more formal logic terminology :

The query asks for

{STUDENT | FOR ALL COURSE : ENROLLED(SID,CID)}

in ENROLLED(), SID is bound to the STUDENT set being restricted and CID is bound to the subject of the FORALL.

(Perhaps as an exercise check how this is almost literally the problem statement.)

By negation of the FORALL, that's the same thing as

{STUDENT | NOT EXISTS COURSE : NOT ENROLLED(SID,CID)}

And the NOT ENROLLED() condition being true is marked by the absence (that is, non-existence) of the concerned SID,CID row. Again as an exercise, check how this is almost literally the SQL solution.

Maybe a different aproach is easier to understand.

This is your example for SQLite:

PRAGMA foreign_keys = ON;

create table student (
  SID text primary key,
  SName text
);

insert into student (SID, SName) values ('S1', 'Amy');
insert into student (SID, SName) values ('S2', 'Tracy');

create table course (
  CID text primary key,
  coursename text
);

insert into course (CID, coursename) values ('C1', 'computer');
insert into course (CID, coursename) values ('C2', 'Biology');

create table enrolled (
  SID text references student (SID),
  CID text references course (CID),
  primary key (SID, CID)
);

insert into enrolled (SID, CID) values ('S1', 'C1');
insert into enrolled (SID, CID) values ('S1', 'C2');
insert into enrolled (SID, CID) values ('S2', 'C1');

You want those students, which did not miss a course. SID and CID are the primary key of enrolled . This means a student can not have the same course twice. And this means you just have to count the courses. If the number of courses per student is the same as the number of course at all, the student has all courses.

This is the number of all courses:

select count(*) from course;

And this is the number of courses of each student:

select count(SName) n, SName from student s
left join enrolled e on s.SID = e.SID
group by SName;

And this is the list of students you are looking for:

select SName from (
  select count(SName) n, SName from student s
  left join enrolled e on s.SID = e.SID
  group by SName)
where n = (select count(*) from course);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM