tags:

views:

56

answers:

3

Say, in a typical university's Database System, what might be a SQL statement that can find all classes with no student registering in them yet? (when registration process is for 10 days and it is now the 3rd day).

What might be other SQL statement solutions?

Details:

Assuming the university have many Departments, and each Departments with various classes, such as Psychology 1A, taught by several different teachers at different time, and Psychology 10, Math 110, etc.

+2  A: 

Here is one option using FULL OUTER JOIN:

SELECT * 
FROM Class
  FULL OUTER JOIN StudentClass
   ON Class.Id = StudentClass.ClassId
WHERE StudentClass.ClassId IS NULL

You can also use a subquery with NOT IN:

SELECT * 
FROM Class
WHERE Id NOT IN
(  SELECT ClassId
   FROM StudentClass
)
Oded
With proper indexing, NOT IN queries tends to be very fast.
Philip Kelley
the select where NOT IN ... good choice, thus limiting ONLY those with nobody registered. I just hope their database structure has a surrogate "ID" key per class offering as you are implying here too.
DRapp
i am refreshing some ideas in SQL... will a LEFT Outer Join be enough to do the job? Considering that some DBMS don't support Full Outer join. Also, does the subquery return duplicates ClassID?
動靜能量
@Jian Lin - LEFT Outer should be OK. And it will return multiple ClassID.
Oded
@Oded So would adding Distinct help for the subquery? The resulting table can be a lot smaller
動靜能量
+1  A: 

You can use a left join, which sticks in NULLs when there's nothing to join

SELECT class
FROM classes c
    LEFT JOIN students s ON c.class = s.class
WHERE s.name IS NULL
thecoop
Students table has student's name and address and ID... won't there be redundant data each time if it also contains registration details for each of his class?
動靜能量
+3  A: 

There are for basic techniques for identifying records in table A whic aren't in table B. Each has its virtues, and each may perform well or badly, depending on data distribution and volume. Also the presence of nulls in the filtering columns can lead to different results (although as it seems unlikely this is a consideration in the specific scenario described in the question). Finally, not every flavour of SQL will support all of these syntaxes.

The most straigthforward is probably the NOT IN:

select *  
from   classes 
where id not in 
        (  select class_id 
           from registrations 
        ) 
/

Next is the related NOT EXISTS:

select *  
from   classes 
where not exists 
        (  select null
           from registrations 
           where registrations.class_id = classes.id
        ) 
/

Then there is the ANTI-JOIN:

select *  
from classes 
     full outer join registrations 
            on (classes.id = registrations.class_id )
where registrations.class_id is null 
/

Lastly there is the MINUS operator:

select id  
from classes 
minus
select class_id 
from registrations
/

This final syntax is quite speedy to write, but obviously falls down in the details. If we want more columns from CLASSES, columns which aren't in REGISTRATIONS, then we need to feed it as a sub-query to a select on CLASSES. So, it is not as widely applicable as the other syntaxes but useful on occasions.

APC
would the subquery in the first solution speed up if "select class_id from registrations" is added with a DISTINCT so that the resulting table has no duplicates?
動靜能量
DISTINCT requires a sort, which costs CPU cycles, and possibly I/O if the sub-query is so large it cannot be sorted in memory. We want the sub-query to be as efficient as possible because it doesn't matter whether the row in CLASSES matches one row in REGISTRATIONS or one hundred. One is enough.
APC