SQL - ayant VS où

202

J'ai les deux tableaux suivants:

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

Je veux trouver le professeur avec le plus de spécialisation. Lorsque j'essaie, cela ne fonctionne pas:

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

Mais quand j'essaye, ça marche:

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

Quelle est la raison? Merci.

Adam Sh
la source
2
Pouvez-vous préciser quelle version de SQL vous utilisez (MySQL, MS SQL, PostgreSQL, Oracle, etc.). En outre, lorsque vous dites "ne fonctionne pas", voulez-vous dire que les résultats ne sont pas ceux que vous attendez ou qu'il y a une erreur de compilation / analyse?
jklemmack
2
Pourquoi utilisez-vous TOUS au lieu de MAX?. Y a-t-il un avantage?
skan

Réponses:

352

WHEREclause introduit une condition sur les lignes individuelles ; HAVINGLa clause introduit une condition sur les agrégations , c'est-à-dire les résultats de la sélection où un seul résultat, tel que nombre, moyenne, min, max ou somme, a été produit à partir de plusieurs lignes. Votre requête appelle un deuxième type de condition (c'est-à-dire une condition sur une agrégation) HAVINGfonctionne donc correctement.

En règle générale, utilisez WHEREavant GROUP BYet HAVINGaprès GROUP BY. C'est une règle assez primitive, mais elle est utile dans plus de 90% des cas.

Pendant que vous y êtes, vous souhaiterez peut-être réécrire votre requête à l'aide de la version ANSI de la jointure:

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)

Cela éliminerait ce WHEREqui était utilisé comme condition de jointure thêta .

dasblinkenlight
la source
40

HAVINGfonctionne sur des agrégats. Étant donné qu'il COUNTs'agit d'une fonction d'agrégation, vous ne pouvez pas l'utiliser dans une WHEREclause.

Voici quelques lectures de MSDN sur les fonctions d'agrégation.

Daniel Mann
la source
30

Tout d'abord, nous devons connaître l'ordre d'exécution des clauses, c'est-à-dire DE> OERE> GROUP BY> AYANT> DISTINCT> SELECT> ORDER BY. Étant donné que la clause WHERE est exécutée avant la clause GROUP BY, les enregistrements ne peuvent pas être filtrés en appliquant WHERE à des enregistrements appliqués GROUP BY .

"HAVING est identique à la clause WHERE mais est appliqué aux enregistrements groupés".

la clause WHERE récupère d'abord les enregistrements en fonction de la condition, puis la clause GROUP BY les regroupe en conséquence, puis la clause HAVING récupère les enregistrements de groupe en fonction de la condition ayant.

Pardhu
la source
Cet ordre des opérations est-il toujours utilisé? Que faire si l'optimiseur de requêtes modifie l'ordre?
MSIS
1
@MSIS même si l'optimiseur de requêtes modifie l'ordre, le résultat doit être le même que si cet ordre était suivi. C'est un ordre logique.
Stephen
18
  1. La clause WHERE peut être utilisée avec les instructions SELECT, INSERT et UPDATE, tandis que HAVING ne peut être utilisé qu'avec l'instruction SELECT.

  2. WHERE filtre les lignes avant l'agrégation (GROUP BY), alors qu'AVOIR les groupes de filtres après les agrégations sont effectuées.

  3. La fonction d'agrégation ne peut être utilisée dans la clause WHERE que si elle se trouve dans une sous-requête contenue dans la clause HAVING, tandis que les fonctions d'agrégation peuvent être utilisées dans la clause HAVING.

La source

Venkata Krishna Reddy
la source
11

Je n'ai pas vu d'exemple des deux dans une seule requête. Cet exemple pourrait donc aider.

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

Cela filtre d'abord le tableau par companyId, puis le regroupe (par pays et par ville) et le filtre également en fonction des agrégations de villes du Mexique. Le companyId n'était pas nécessaire dans l'agrégation, mais nous avons pu utiliser WHERE pour filtrer uniquement les lignes souhaitées avant d'utiliser GROUP BY.

Nhan
la source
ce n'est pas un bon exemple car vous pourriez convertir: `WHERE companyId = 884501253109 GROUP BY country, city HAVING country = 'MX' 'to:` WHERE companyId = 884501253109, country =' MX 'GROUP BY city'
Etienne Herlaut
Si vous déplacez simplement le filtrage [country] vers le WHERE que vous avez suggéré, la requête entraînera une erreur de SELECT [country], car [country] n'est plus inclus dans l'agrégation GROUP BY et ne peut donc pas être sélectionné.
Nhan
Votre point sur l'optimisation est pris sur le déplacement de [pays] vers le WHERE car ce serait un ensemble de données plus petit sur GROUP BY avec plus tard. Bien sûr, ce n'est qu'un exemple pour illustrer les utilisations possibles. Nous pouvons changer pour avoir une somme (total)> 1000 et ce serait un cas tout à fait valable pour inclure OERE et AVOIR.
Nhan
9

Vous ne pouvez pas utiliser la clause where avec des fonctions d'agrégation, car lorsque vous récupérez des enregistrements sur la base de la condition, elle va dans la table enregistrement par enregistrement, puis récupère l'enregistrement sur la base de la condition que nous avons donnée. Donc, cette fois, nous ne pouvons pas où clause. Tout en ayant la clause fonctionne sur le resultSet que nous obtenons finalement après avoir exécuté une requête.

Exemple de requête:

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

Cela stockera le resultSet dans une mémoire temporaire, puis la clause having effectuera son travail. Nous pouvons donc facilement utiliser des fonctions d'agrégation ici.

Akash5288
la source
2
Je pense que nous ne pouvons pas utiliser la clause HAVING sans la clause GROUP BY. Position de la clause HAVING - SELECT -> FROM -> WHERE -> GROUP BY -> HAVING -> ORDER BY
Morez
4

1. Nous pouvons utiliser la fonction d'agrégation avec la clause HAVING et non par la clause WHERE, par exemple min, max, avg.

2. La clause WHERE élimine l'enregistrement tuple par tuple La clause HAVING élimine le groupe entier de la collection de groupe

La plupart du temps HAVING est utilisé lorsque vous avez des groupes de données et WHERE est utilisé lorsque vous avez des données en lignes.

Mihir Trivedi
la source