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
Чому ви використовуєте ВСІ замість MAX ?. Чи є якась перевага?
скан

Відповіді:


351

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те, що було використано як умову приєднання тета .


39

HAVINGпрацює на агрегатах. Оскільки COUNTце сукупна функція, ви не можете використовувати її в WHEREпункті.

Ось кілька читань з MSDN про сукупні функції.


30

Перш за все, ми повинні знати порядок виконання пунктів, тобто ВІД> ГДО> ГРУПА ПО> ВІДХОДЖЕННЯ> РОЗМІСТЬ> ВИБІР> ЗАМОВИТИ ЗА. Оскільки пункт WHERE виконується перед пунктом GROUP BY, записи не можна відфільтрувати, застосувавши WHERE до застосованих записів GROUP BY .

"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

Це фільтрує таблицю спочатку компанієюId, потім групує її (за країною та містом) і додатково відфільтровує її до лише міських сукупностей Мексики. КомпаніяId не потрібна була в агрегації, але ми змогли використати WHERE для фільтрації лише потрібних рядків, перш ніж використовувати GROUP BY.


це не гарний приклад, як ви могли конвертувати: `WHERE companyId = 884501253109 ГРУПА по країні, місті HAVING country = 'MX' 'в: WHERE companyId = 884501253109, країна =' MX 'GROUP BY
city`

Якщо просто перемістити [країну] фільтрацію до місця, де ви запропонували, запит буде помилковим в SELECT [країна], оскільки [країна] більше не включається до групи GROUP BY, тому не може бути вибрано.
Нян

Ваша точка оптимізації приймається при переміщенні [країни] до WHERE, оскільки це буде меншим набором даних для GROUP BY з пізнішими. Звичайно, це лише приклад для ілюстрації можливого використання. Ми можемо змінити на HAVING суму (загальну)> 1000, і це було б цілком справедливим випадком, щоб включити туди, де і що є.
Нхан

9

Ви не можете використовувати там, де пункт із сукупними функціями, оскільки там, де виймає записи на основі умови, він переходить у табличний запис за записом, а потім отримує запис на основі умови, яку ми надаємо. Тож того часу ми не можемо де застерегти. У той час, як застереження працює на resultSet, який ми нарешті отримуємо після запуску запиту.

Приклад запиту:

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

Це збереже resultSet у тимчасовій пам'яті, після чого, маючи пункт, виконує свою роботу. Тому ми можемо легко використовувати тут сукупні функції.


2
Я думаю, що ми не можемо використовувати пункт HAVING без пункту GROUP BY. Позиція положення HAVING - ВИБІР -> ВІД -> ДЕ -> ГРУПА ПО -> HAVING -> ЗАМОВИТИ
Morez

4

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

2. WHERE пункт виключає кортеж запису за допомогою tuple HAVING пункт виключає всю групу з колекції групи

Здебільшого HAVING використовується, коли у вас є групи даних, і WHERE використовується, коли у вас є дані в рядках.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.