0

我有 2 个简单的表:students(sno,sname,age)take(sno,cno). TakeN-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;  

它与MAXANDHAVING的处理顺序有关吗?

一个简单的方法是使用子查询:

SELECT * FROM students  
WHERE sno NOT IN  
(SELECT sno FROM take WHERE cno = 'CS112');  

但我有兴趣了解使用的版本JOIN

4

4 回答 4

1

如果有课程,则CASE内部MAX产生 1,如果没有课程,则产生 0。MAX对每一行取CASE1,如果学生有任何行的值为 1(意味着他们有班级),则为 1,否则为 0。通过说他们必须HAVINGMAX > 1你是说他们必须上课。以最复杂的方式。

于 2013-04-16T21:14:02.410 回答
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. 相反,您必须使用完整的表达式。

于 2013-04-16T21:43:01.037 回答
0

The MAX ensures that if only one of the classes the student is taking is CS112, then it fails the HAVING statement and won't return that student.

于 2013-04-16T21:12:23.783 回答
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,然后我们对其进行过滤(过滤掉学生确实参加过课程的情况)。

于 2013-04-16T21:20:02.943 回答