Je suis en train de réécrire des requêtes qui ne récupèrent plus toutes les données requises. Ma question concerne une pratique que je n'ai jamais vue et je n'ai trouvé aucune question sur StackExchange qui traite spécifiquement du problème.
Je sais que le point de l'instruction HAVING
est d'introduire des conditions sur les agrégations, tout comme WHERE
introduit des conditions sur des lignes individuelles. Cependant, ce que je vois dans ce code est HAVING
utilisé à la place de WHERE
dans les requêtes avec agrégations. Les conditions dans HAVING
ne s'appliquent pas aux agrégations, mais aux colonnes non agrégées.
Par exemple:
SELECT id, filedate, SUM(amount)
FROM Sales
GROUP BY id, filedate
HAVING id = 123 AND filedate = '1/1/2018'
Par opposition à:
SELECT id, filedate, SUM(amount)
FROM Sales
WHERE id = 123 AND filedate = '1/1/2018'
GROUP BY id, filedate
Y a-t-il des implications sur les performances ou d'autres avantages/inconvénients de cette stratégie?
Je n'ai pas essayé d'exécuter les diagnostics moi-même, ce n'est pas une priorité et je devrais le faire à mon propre rythme. Cependant, je pense que je peux le faire s'il n'y a pas de réponse claire à ce sujet.
Ma préoccupation est de savoir comment l'optimiseur voit cette requête. Agrège-t-il toutes les données et restreint-il ensuite l'ensemble de résultats en fonction de la clause HAVING
, ou réalise-t-il qu'il peut appliquer les conditions ayant sur les lignes individuelles car elles font spécifiquement référence à des colonnes non agrégées?
EDIT: Pour mes exemples de requêtes et le SQL réel que je réécris, les plans sont identiques, mais les requêtes sont de complexité similaire et je ne suis pas encore suffisamment informé pour tirer des conclusions des plans identiques.
Les conditions dans
HAVING
ne s'appliquent pas aux agrégations, mais aux colonnes non agrégées.
Le problème ici est de savoir comment vous décrivez à quoi s'applique la clause HAVING
. La clause HAVING
s'applique toujours aux champs agrégés , qui est toutes les colonnes restantes post - agrégation. Vous essayez de montrer/dire que la clause HAVING
n'est appliquée à aucune fonction d'agrégation , ce à quoi elles s'appliquent généralement . Mais en réalité, la clause HAVING
régit le résultat de cette fonction d'agrégation, ou, dans votre premier exemple, le résultat de la colonne de regroupement. Mais dans les deux cas, l'agrégation a déjà été effectuée.
Donc, en termes de performances (sans parler de la lisibilité pour les autres essayant de mettre à jour ce code plus tard), vous utilisez la clause WHERE
pour filtrer jusqu'à ce que sera agrégé, puis la clause HAVING
pour filtrer ce qui a été agrégé. Et, alors que le résultat d'un test simple comme indiqué dans la question masque la différence entre le moment des deux (ou le placement logique dans la séquence dans laquelle la requête est traitée) de telle sorte qu'ils "semblent" faire la même chose, Je serais assez surpris s'il n'était pas moins efficace d'agréger un tas de lignes que de les jeter plus tard, alors qu'elles auraient logiquement pu être éliminées avant de stocker/calculer les agrégations. CEPENDANT, si vous voyez que leurs plans d'exécution sont similaires pour cet exemple simple, je suis prêt à parier que c'est simplement parce que l'optimiseur voit qu'il serait plus efficace de rendre ces HAVING
conditions réelles WHERE
conditionne la réécriture de la requête avant de l'exécuter. Mais dans ce cas, je déconseille toujours d'écrire des requêtes de cette façon car vous faites en sorte que l'optimiseur prenne plus de temps pour réécrire le mauvais code alors qu'il devrait passer ce temps/cycles CPU à trouver un plan plus efficace. @ DavidSpillett ajouté (dans un commentaire sur cette réponse): "De plus, vous comptez sur le planificateur de requêtes pour voir le potentiel d'optimisation, ce qu'il ne peut pas dans des requêtes plus complexes ou si votre code finit par être porté sur une autre base de données (ou même simplement une ancienne version de SQL Server) ".
Pour ce que ça vaut, même Microsoft documentation pour la clause HAVING a déclaré qu'il agissait comme une clause WHERE
quand aucun GROUP BY
était présent. Maintenant que la documentation est sur GitHub, j'ai pu la corriger récemment via Pull Request # 235: Corriger et améliorer la clause HAVING .
Salomon donne de très bonnes explications, mais pour moi, la réponse simple est de se souvenir de l'ordre de traitement logique des requêtes SQL comme l'a écrit Itzik Ben-Gan ici La séquence est toujours
DE -> OERE -> GROUP BY -> AYANT -> SELECT -> ORDER BY
Donc, vous voyez, si nous pouvons appliquer un filtre WHERE avant GROUP BY, nous pouvons réduire la quantité de données à traiter par GROUP BY, en particulier, l'opération WHERE peut être extrêmement efficace lorsque des index appropriés existent. En tant que tel, je dirais que si l'utilisation de WHERE et HAVING renvoie le même résultat du point de vue commercial, WHERE est toujours gagnant par rapport à HAVING.