265 votes

SQL - HAVING et WHERE

J'ai les deux tableaux suivants :

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

Je veux trouver le professeur qui a le plus de spécialisation. Lorsque j'essaie de le faire, 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'essaie ça, ç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 en est la raison ? Merci.

3 votes

Pouvez-vous préciser quelle version de SQL vous utilisez (MySQL, MS SQL, PostgreSQL, Oracle, etc.). De plus, 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/parse ?

3 votes

Pourquoi utilisez-vous ALL au lieu de MAX ?. Y a-t-il un avantage ?

439voto

dasblinkenlight Points 264350

WHERE introduit une condition sur lignes individuelles ; HAVING introduit une condition sur agrégations c'est-à-dire les résultats de la sélection où un seul résultat, tel que le nombre, la moyenne, le minimum, le maximum ou la somme, a été produit à partir des données suivantes multiple rangs. Votre requête requiert un deuxième type de condition (c'est-à-dire une condition sur une agrégation), à savoir HAVING fonctionne correctement.

En règle générale, utilisez WHERE avant GROUP BY et HAVING après GROUP BY . Il s'agit d'une règle plutôt primitive, mais elle est utile dans plus de 90% des cas.

Pendant que vous y êtes, vous pouvez réécrire votre requête en utilisant 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 permettrait d'éliminer WHERE qui a été utilisé comme un theta joint condition .

0 votes

Les performances sont-elles réduites si l'on utilise where après group by ?

61voto

Pardhu Points 521

Tout d'abord, nous devons connaître l'ordre d'exécution des clauses, à savoir FROM > WHERE > GROUP BY > HAVING > DISTINCT > SELECT > ORDER BY. Depuis La clause est exécutée avant GROUPE PAR Clause les enregistrements ne peuvent pas être filtrés en appliquant à un GROUPE PAR les dossiers appliqués.

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

d'abord le récupère les enregistrements en fonction de la condition, alors la clause GROUPE PAR les regroupe en conséquence, puis la clause AYANT permet d'extraire les enregistrements du groupe en fonction de la condition d'existence.

1 votes

Cet ordre des opérations est-il toujours utilisé ? Que se passe-t-il si l'optimiseur de requêtes change l'ordre ?

5 votes

@MSIS même si l'optimiseur de requêtes change l'ordre, le résultat devrait être le même. comme si cet ordre a été suivi. C'est un ordre logique.

43voto

Daniel Mann Points 9146

HAVING fonctionne sur les agrégats. Depuis COUNT est une fonction d'agrégation, vous ne pouvez pas l'utiliser dans une fonction WHERE clause.

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

27voto

  1. La clause WHERE peut être utilisée avec les instructions SELECT, INSERT et UPDATE, tandis que la clause HAVING ne peut être utilisée qu'avec l'instruction SELECT.

  2. WHERE filtre les lignes avant l'agrégation (GROUP BY), tandis que HAVING filtre les groupes après que les agrégations ont été effectuées.

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

Source :

12voto

Nhan Points 1225

Je n'ai pas vu d'exemple des deux en une seule requête. Cet exemple pourrait donc vous 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

Cette méthode permet de filtrer la table en fonction de l'identifiant de la société, puis de la regrouper (par pays et par ville) et enfin de la filtrer en fonction des agrégations de villes du Mexique. L'ID de la société n'était pas nécessaire dans l'agrégation, mais nous avons pu utiliser WHERE pour filtrer uniquement les lignes que nous voulions avant d'utiliser GROUP BY.

0 votes

Ce n'est pas un bon exemple car vous pourriez convertir : ` WHERE companyId = 884501253109 GROUP BY country, city HAVING country = 'MX' ` en : ` WHERE companyId = 884501253109, country = 'MX' GROUP BY city `

0 votes

Si l'on déplace simplement le filtrage [pays] vers le WHERE que vous avez suggéré, la requête se trompera de SELECT [pays], car [pays] n'est plus inclus dans l'agrégation GROUP BY et ne peut donc pas être sélectionné.

0 votes

Votre point sur l'optimisation est pris en compte pour déplacer [pays] vers le WHERE, car cela constituerait un ensemble de données plus petit pour GROUP BY par la suite. Bien sûr, il ne s'agit que d'un exemple pour illustrer les utilisations possibles. Nous pouvons changer pour HAVING sum(total) > 1000 et ce serait un cas tout à fait valable pour inclure WHERE et HAVING.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X