我有两个简单的表:students(sno,sname,age)
和take(sno,cno)
。 Take
是学生与课程之间N-N
关系的表格
我想找到不参加特定课程的学生。
以下查询完成了这项工作,但我不清楚它是如何工作的:
SELECT s.sno,s.sname,s.age
FROM students s LEFT JOIN take t
ON (s.sno = t.sno)
GROUP BY s.sno,s.sname,s.age
HAVING MAX(CASE WHEN t.cno = 'CS112' THEN 1 ELSE 0 END) = 0;
是否与处理MAX
和HAVING
的订单有关?
执行此操作的一个简单方法是使用子查询:
SELECT * FROM students
WHERE sno NOT IN
(SELECT sno FROM take WHERE cno = 'CS112');
但我有兴趣使用JOIN
答案 0 :(得分:1)
如果他们有课程,CASE
内的MAX
会产生1,否则会产生0。对每一行取MAX
CASE
,如果学生的任何行为1(意味着他们有类),则得1,否则为0。说他们必须HAVING
MAX
> 1
,你说他们必须上课。以最复杂的方式。
答案 1 :(得分:1)
如果您将max()
替换为sum()
,这可能会更容易理解。
考虑这个select
声明:
SELECT s.sno, s.sname,s.age, SUM(CASE WHEN t.cno = 'CS112' THEN 1 ELSE 0 END) as NumCS112
新专栏NumCS112具有学生参加课程的次数。接下来,将其放在having
子句中:
HAVING NumCS112 = 0
嗯,这意味着学生参加该课程的次数是0 - 所以学生没有参加该课程。
你可以用max()
做同样的事情,你得到一个标志而不是一个计数。所以:
SELECT s.sno, s.sname,s.age, MAX(CASE WHEN t.cno = 'CS112' THEN 1 ELSE 0 END) as HasTaken_CS112
. . .
HAVING HasTaken_CS112 = 0
但是,您没有select
子句中的表达式,因此您无法使用HasTaken_CS112
。相反,您必须使用完整的表达式。
答案 2 :(得分:0)
MAX确保如果学生只选择其中一个班级是CS112,那么它将失败HAVING语句并且不会返回该学生。
答案 3 :(得分:0)
您可以在JOIN条件中添加cno过滤器而不是where,并在连接的RIGHT部分中查找NULL值(表示不匹配)。
SELECT s.sno, s.sname, s.age
FROM students s LEFT JOIN take t
ON s.sno = t.sno AND t.cno = 'CS112'
WHERE t.cno IS NULL
由于cno上的过滤器处于JOIN状态,因此它实际上不会过滤掉学生(而不是WHERE子句中的过滤器)。如果不匹配,则连接的Take部分将包含NULL,然后我们会过滤掉(过滤掉学生确实参加课程的情况)。