SQL - mając VS gdzie

202

Mam następujące dwie tabele:

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

Chcę znaleźć wykładowcę o największej specjalizacji. Kiedy próbuję tego, to nie działa:

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);

Ale gdy spróbuję, działa:

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); 

Jaki jest powód? Dzięki.

Adam Sh
źródło
2
Czy możesz wyjaśnić, której wersji SQL używasz (MySQL, MS SQL, PostgreSQL, Oracle itp.). Ponadto, gdy mówisz „nie działa”, czy masz na myśli, że wyniki nie są zgodne z oczekiwaniami lub że wystąpił błąd kompilacji / analizy?
jklemmack
2
Dlaczego używasz ALL zamiast MAX ?. Czy jest jakaś zaleta?
skan

Odpowiedzi:

351

WHEREklauzula wprowadza warunek w poszczególnych wierszach ; HAVINGklauzula wprowadza warunek dotyczący agregacji , tj. wyników selekcji, w których pojedynczy wynik, taki jak liczba, średnia, min, maks lub suma, został wygenerowany z wielu wierszy. Twoje zapytanie wymaga drugiego rodzaju warunku (tj. Warunku agregacji), a zatem HAVINGdziała poprawnie.

Z reguły należy stosować WHEREprzed GROUP BYi HAVINGpo GROUP BY. Jest to raczej prymitywna reguła, ale jest przydatna w ponad 90% przypadków.

W tym momencie możesz ponownie napisać zapytanie, używając wersji złączenia 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)

To wyeliminowałoby to, WHEREco zostało użyte jako warunek połączenia theta .

dasblinkenlight
źródło
39

HAVINGdziała na agregatach. Ponieważ COUNTjest to funkcja agregująca, nie można jej użyć w WHEREklauzuli.

Oto kilka lektur z MSDN na temat funkcji agregujących.

Daniel Mann
źródło
30

Najpierw powinniśmy znać kolejność wykonywania Klauzul, tj. Z> GDZIE> GRUPUJ WEDŁUG> POSIADAJĄ> WYRÓŻNIJ> WYBIERZ> ZAMÓW WG. Ponieważ klauzula WHERE jest wykonywana przed klauzulą GROUP BY, rekordy nie mogą być filtrowane poprzez zastosowanie WHERE do zastosowanych rekordów GROUP BY .

„HAVING jest taki sam jak klauzula WHERE, ale jest stosowany do zgrupowanych rekordów”.

najpierw klauzula WHERE pobiera rekordy na podstawie warunku, następnie klauzula GROUP BY odpowiednio je grupuje, a następnie klauzula HAVING pobiera rekordy grupy na podstawie warunku posiadania.

Pardhu
źródło
Czy ta kolejność operacji jest zawsze używana? Co się stanie, jeśli optymalizator zapytań zmieni kolejność?
MSIS,
1
@ MSIS, nawet jeśli optymalizator zapytań zmienia kolejność, wynik powinien być taki sam, jak gdyby kolejność była przestrzegana. To logiczny porządek.
Stephen
18
  1. Klauzula WHERE może być używana z instrukcjami SELECT, INSERT i UPDATE, natomiast HAVING może być używana tylko z instrukcją SELECT.

  2. GDZIE filtruje wiersze przed agregacją (GROUP BY), natomiast HAVING filtruje grupy po agregacji.

  3. Funkcji agregującej nie można używać w klauzuli WHERE, chyba że znajduje się ona w podzapytaniu zawartym w klauzuli HAVING, natomiast funkcji agregujących można używać w klauzuli HAVING.

Źródło

Venkata Krishna Reddy
źródło
11

Nie widziałem przykładu obu w jednym zapytaniu. Ten przykład może pomóc.

  /**
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

To najpierw filtruje tabelę według nazwy firmy, a następnie grupuje ją (według kraju i miasta), a dodatkowo filtruje do tylko agregacji miast w Meksyku. Firma nie była potrzebna w agregacji, ale mogliśmy użyć GDZIE, aby odfiltrować tylko wiersze, które chcieliśmy przed użyciem GROUP BY.

Nhan
źródło
nie jest to dobry przykład, ponieważ można przekonwertować: `WHERE companyId = 884501253109 GROUP GROUP BY country, city HAVING country = 'MX' 'na:` WHERE companyId = 884501253109, country =' MX 'GROUP BY city'
Etienne Herlaut
Jeśli przeniesienie filtrowania [kraj] do GDZIE zasugerowałeś, zapytanie spowoduje błąd z WYBIERZ [kraj], ponieważ [kraj] nie jest już uwzględniony w agregacji GROUP BY, dlatego nie można go wybrać.
Nhan
Twój punkt optymalizacji jest brany pod uwagę przy przenoszeniu [kraju] do GDZIE, ponieważ byłby to mniejszy zestaw danych do GROUP BY z późniejszym. Oczywiście jest to tylko przykład ilustrujący możliwe zastosowania. Możemy zmienić na HAVING suma (ogółem)> 1000 i byłby to całkowicie uzasadniony przypadek, obejmujący GDZIE i POSIADANIE.
Nhan,
9

Nie można użyć klauzuli where z funkcjami agregującymi, ponieważ skąd pobierają rekordy na podstawie warunku, przechodzą do tabeli rekord po rekordzie, a następnie pobierają rekord na podstawie podanego przez nas warunku. Więc tym razem nie możemy dokąd klauzula. Posiadanie klauzuli działa na zestawie wyników, który w końcu otrzymujemy po uruchomieniu zapytania.

Przykładowe zapytanie:

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

Spowoduje to zapisanie scoreSet w pamięci tymczasowej, a następnie posiadanie klauzuli wykona swoją pracę. Dzięki temu możemy łatwo korzystać z funkcji agregujących.

Akash5288
źródło
2
Myślę, że nie możemy użyć klauzuli HAVING bez klauzuli GROUP BY. Pozycja klauzuli HAVING - WYBIERZ -> OD -> GDZIE -> GRUPUJ WEDŁUG -> POSIADAJĄC -> ORDER BY
Morez
4

1. Możemy użyć funkcji agregującej z klauzulą ​​HAVING, a nie klauzulą ​​WHERE, np. Min, max, avg.

2. GDZIE klauzula eliminuje rekord krotki przez krotkę klauzula HAVING eliminuje całą grupę z kolekcji grupy

Najczęściej HAVING jest używany, gdy masz grupy danych, a GDZIE jest używany, gdy masz dane w wierszach.

Mihir Trivedi
źródło