2013-04-16 51 views
0

我有2個簡單的表格:students(sno,sname,age)take(sno,cno)Take是學生和課程之間的N-N關係表。
我想找到沒有參加特定課程的學生。與HAVING MAX的查詢如何實際正確地工作?

下面的查詢做的工作,但它是我不清楚它是如何工作:

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; 

它是否有被處理的順序的MAXHAVING辦?

一個不重要的方式做這將是與子查詢:

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

但我有興趣瞭解使用JOIN

回答

0

最大保證的版本,如果只有其中一個類的學生服用的是CS112,那麼它不符合HAVING聲明,並且不會返回該學生。

1

MAX內部的CASE如果他們有課程則產生1,如果他們沒有則產生0。以CASEMAX爲單位,如果學生有1行(意思是他們有這個類),則返回1,否則返回0。說他們必須HAVINGMAX> 1,你說他們必須有班。以最複雜的方式可能。

+0

'爲每行取最大值。行還是組?我可以隱約地理解它做的是類似的東西。我試圖理解實際的步驟和中間計算。 – Cratylus

+0

對於每一行('-GROUP'前面),它都會運行該案例。然後,在彙總過程中,它將'MAX'作爲彙總的一部分,爲每個組提供最大值。最後,它會過濾出符合'HAVING'要求的行。 – Adrian

+0

'CASE'總是先運行?我們如何知道訂單? – Cratylus

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子句中而不是一個過濾器)。在不匹配的情況下,加入的部分將包含NULL,然後我們會過濾(過濾出學生確實參加課程的情況)。

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。相反,你必須使用完整的表達式。