2013-04-16 3 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; 

ли это делать с Поручения MAX И HAVING обрабатываются?

Тривиальным способ сделать это было бы с подзапроса:

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

Но я заинтересован в понимании версии с помощью JOIN

ответ

0

МАХ гарантирует, что если только один из классов студент принимающий CS112, то он не выполняет инструкцию HAVING и не вернет этого ученика.

1

CASE внутри MAX дает 1, если у них есть курс, 0, если они этого не делают. Принимая MAX из этого CASE для каждой строки, получаем 1, если учащийся имеет любую строку, где он будет 1 (что означает, что у них есть класс) и 0 в противном случае. Говоря, что они должны HAVINGMAX> 1, вы говорите, что у них должен быть класс. В самом запутанном виде.

+0

'Взятие MAX этого CASE для каждой строки'. Строка или группа? Я могу смутно понять, что он делает что-то подобное. Я пытаюсь понять фактические шаги и промежуточные вычисления. – Cratylus

+0

Для каждой строки (pre-'GROUP') она запускает этот случай. Затем, во время агрегации, он запускает 'MAX' как часть агрегации, давая max для каждой группы. В конце он отфильтровывает строки, соответствующие требованиям 'HAVING'. – Adrian

+0

«CASE» всегда работает первым? Как мы узнаем заказ? – Cratylus

0

Вы можете добавить cno-фильтр в состояние JOIN вместо того, и где искать значения 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 находится в РЕГИСТРИРУЙТЕСЬ состоянии, он не будет на самом деле отфильтровать студентов (в отличие от фильтра в предложении 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. Вместо этого вы должны использовать полное выражение.

 Смежные вопросы

  • Нет связанных вопросов^_^