SQL - имея VS где


202

У меня есть следующие две таблицы:

1. Lecturers (LectID, Fname, Lname, degree).
2. Lecturers_Specialization (LectID, Expertise).

Я хочу найти лектора с самой специализацией. Когда я пытаюсь это, это не работает:

SELECT
  L.LectID, 
  Fname, 
  Lname 
FROM Lecturers L, 
     Lecturers_Specialization S
WHERE L.LectID = S.LectID
AND COUNT(S.Expertise) >= ALL (SELECT
  COUNT(Expertise)
FROM Lecturers_Specialization
GROUP BY LectID);

Но когда я пытаюсь это, это работает:

SELECT
  L.LectID,
  Fname,
  Lname 
FROM Lecturers L,
     Lecturers_Specialization S
WHERE L.LectID = S.LectID
GROUP BY L.LectID,
         Fname,
         Lname 
HAVING COUNT(S.Expertise) >= ALL (SELECT
  COUNT(Expertise)
FROM Lecturers_Specialization
GROUP BY LectID); 

Какова причина? Спасибо.


2
Можете ли вы уточнить, какую версию SQL вы используете (MySQL, MS SQL, PostgreSQL, Oracle и т. Д.). Кроме того, когда вы говорите «не работает», вы имеете в виду, что результаты не соответствуют ожидаемым, или что есть ошибка компиляции / разбора?
jklemmack

2
Почему вы используете ВСЕ, а не МАКС? Есть ли преимущество?
Скан

Ответы:


352

WHEREпредложение вводит условие для отдельных строк ; HAVINGПредложение вводит условие для агрегатов , то есть результатов выбора, когда один результат, такой как число, среднее, минимальное, максимальное или сумма, был получен из нескольких строк. Ваш запрос требует второго типа условия (то есть условия агрегации), следовательно, HAVINGработает правильно.

Как правило, используйте WHEREдо GROUP BYи HAVINGпосле GROUP BY. Это довольно примитивное правило, но оно полезно в более чем 90% случаев.

Пока вы это делаете, вы можете переписать свой запрос, используя ANSI-версию объединения:

SELECT  L.LectID, Fname, Lname
FROM Lecturers L
JOIN Lecturers_Specialization S ON L.LectID=S.LectID
GROUP BY L.LectID, Fname, Lname
HAVING COUNT(S.Expertise)>=ALL
(SELECT COUNT(Expertise) FROM Lecturers_Specialization GROUP BY LectID)

Это устранило бы WHEREто, что использовалось как условие тета-соединения .


40

HAVINGработает на агрегатах. Поскольку COUNTэто агрегатная функция, вы не можете использовать ее в WHEREпредложении.

Вот некоторые чтения из MSDN о агрегатных функциях.


30

Сначала мы должны знать порядок выполнения предложений, т. Е. ОТ> ГДЕ> ГРУППА BY> HAVING> DISTINCT> SELECT> ORDER BY. Поскольку предложение WHERE выполняется перед предложением GROUP BY, записи не могут быть отфильтрованы путем применения WHERE к примененным записям GROUP BY .

Msgstr "HAVING - это то же самое, что и предложение WHERE, но применяется к сгруппированным записям".

сначала предложение WHERE извлекает записи на основе условия, затем предложение GROUP BY группирует их соответствующим образом, а затем предложение HAVING выбирает записи группы на основе имеющего условия.


Всегда ли используется этот порядок операций? Что если оптимизатор запросов изменит порядок?
MSIS

1
@MSIS, даже если оптимизатор запросов изменяет порядок, результат должен быть таким же, как если бы этот порядок выполнялся. Это логичный порядок.
Стивен

18
  1. Предложение WHERE может использоваться с операторами SELECT, INSERT и UPDATE, тогда как HAVING может использоваться только с оператором SELECT.

  2. WHERE фильтрует строки перед агрегацией (GROUP BY), тогда как HAVING фильтрует группы после агрегации.

  3. Агрегатная функция не может использоваться в предложении WHERE, если она не входит в подзапрос, содержащийся в предложении HAVING, тогда как агрегатные функции могут использоваться в предложении HAVING.

Источник


11

Не видел пример обоих в одном запросе. Так что этот пример может помочь.

  /**
INTERNATIONAL_ORDERS - table of orders by company by location by day
companyId, country, city, total, date
**/

SELECT country, city, sum(total) totalCityOrders 
FROM INTERNATIONAL_ORDERS with (nolock)
WHERE companyId = 884501253109
GROUP BY country, city
HAVING country = 'MX'
ORDER BY sum(total) DESC

Это фильтрует таблицу сначала по идентификатору компании, затем группирует ее (по стране и городу) и дополнительно фильтрует ее по группам городов Мексики. Идентификатор компании не требовался при агрегации, но мы смогли использовать WHERE для фильтрации только тех строк, которые нам нужны, перед использованием GROUP BY.


это не очень хороший пример, который можно преобразовать: `WHERE companyId = 884501253109 GROUP BY страна, город ИМЕЕТ страну = 'MX'` в: `WHERE companyId = 884501253109, страна = 'MX' GROUP BY
city`

Если просто переместить фильтрацию [страна] в ГДЕ, которое вы предложили, запрос выдаст ошибку из SELECT [страна], поскольку [страна] больше не включается в агрегирование GROUP BY, поэтому не может быть выбрана.
Нян

Ваше мнение об оптимизации берется при перемещении [страны] в ГДЕ, поскольку это будет меньший набор данных для GROUP BY с более поздним. Конечно, это всего лишь пример, иллюстрирующий возможные варианты использования. Мы можем изменить на HAVING sum (total)> 1000, и это будет вполне допустимый случай, включающий WHERE и HAVING.
Нян

9

Вы не можете использовать предложение where с агрегатными функциями, потому что, когда выборка производится на основе условия, она переходит в таблицу «запись за записью», а затем выбирает запись на основе условия, которое мы дали. Так что время мы не можем, где оговорка. В то время как наличие условия работает с набором результатов, который мы наконец получаем после выполнения запроса.

Пример запроса:

select empName, sum(Bonus) 
from employees 
order by empName 
having sum(Bonus) > 5000;

Это сохранит resultSet во временной памяти, а затем с предложением выполнит свою работу. Таким образом, мы можем легко использовать агрегатные функции здесь.


2
Я думаю, что мы не можем использовать предложение HAVING без предложения GROUP BY. Положение пункта HAVING - ВЫБРАТЬ -> ОТ -> ГДЕ -> ГРУППИТЬ ПО -> ЗАДАТЬ -> ЗАКАЗАТЬ
Morez

4

1. Мы можем использовать агрегатную функцию с предложением HAVING, а не с предложением WHERE, например, min, max, avg.

2. Предложение WHERE исключает кортеж записи с помощью предложения HAVING исключает всю группу из коллекции группы

В основном HAVING используется, когда у вас есть группы данных, и WHERE используется, когда у вас есть данные в строках.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.