Pourquoi MySQL permet-il à HAVING d'utiliser des alias SELECT?

14

En SQL, pour autant que je sache, l'ordre de traitement des requêtes logiques, qui est l'ordre d'interprétation conceptuelle, commence par FROM de la manière suivante:

  1. DE
  2. PAR GROUPE
  3. AYANT
  4. SÉLECTIONNER
  5. COMMANDÉ PAR

Après cette liste, il est facile de comprendre pourquoi vous ne pouvez pas avoir d'alias SELECT dans une clause WHERE, car l'alias n'a pas encore été créé. T-SQL (SQL Server) suit strictement cela et vous ne pouvez pas utiliser les alias SELECT jusqu'à ce que vous ayez passé SELECT.

Mais dans MySQL, il est possible d'utiliser des alias SELECT dans la clause HAVING même s'il doit (logiquement) être traité avant la clause SELECT. Comment cela est-il possible?

Pour donner un exemple:

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;

L'instruction n'est pas valide dans T-SQL (car HAVING fait référence à l'alias SELECT Amount) ...

Msg 207, Level 16, State 1, Line 5
Invalid column name 'Amount'.

... mais fonctionne très bien dans MySQL.

Sur cette base, je me demande:

  • MySQL prend-il un raccourci dans les règles SQL pour aider l'utilisateur? Peut-être en utilisant une sorte de pré-analyse?
  • Ou est-ce que MySQL utilise un ordre d'interprétation conceptuelle différent de celui que je pensais que tous les SGBDR suivaient?
Ohlin
la source
1
Je suppose que c'est votre deuxième puce.
a_horse_with_no_name
3
Eh bien, je suppose que cela ne provoque aucune ambiguïté ou confusion tant qu'ils ne prennent pas en charge les fonctions de classement. Alors SELECT C, ROW_NUMBER() OVER (ORDER BY X) AS RN FROM T GROUP BY C HAVING RN = 1sera problématique car les ROW_NUMBERruns après leHAVING
Martin Smith
Je ne sais pas quelles fonctions de classement sont prises en charge par MySQL. Si vous voulez que le numéro de ligne , vous devez créer de cette manière: SELECT @rownum:=@rownum + 1 as row .... Peut-être que la raison pour laquelle ils prennent en charge les alias SELECT est simplement parce qu'ils le peuvent, car ils ne prennent pas en charge les éléments qui rendraient cela impossible ... qui sait? :)
Ohlin
Comme l'explique @MartinSmith, tant qu'il n'y a pas de fonctions fenêtre / classement, l'ordre logique d'exécution de HAVINGet la SELECTclause peuvent être échangés. Ainsi, il n'y a aucune ambiguïté en faisant cela et peut simplifier l'apparence du code lorsqu'il y a des expressions monstrueuses dans SELECT.
ypercubeᵀᴹ
J'espère que c'est un peu sur le sujet de dire que j'ai répondu à une question ici qui bénéficie de résultats plus rapides (avec distincts) ... avec Alias in the Havingmalgré la même Explainsortie. Il se produit donc des variations avec l'Optimizer.
Drew

Réponses:

13

Eh bien, lorsque vous avez une question de ce genre, la meilleure source d'informations à mon humble avis est la documentation MySQL. Maintenant au point. C'est le comportement de l'extension MySql GROUP BYqui est activé par défaut.

Extensions MySQL à GROUP BY
MySQL étend ce comportement pour permettre l'utilisation d'un alias dans la clause HAVING pour la colonne agrégée

Si vous voulez un comportement standard, vous pouvez désactiver cette extension avec sql_mode ONLY_FULL_GROUP_BY

SET [SESSION | GLOBAL] sql_mode = ONLY_FULL_GROUP_BY;

Si vous essayez d'exécuter la requête mentionnée ci-dessus dans ONLY_FULL_GROUP_BYsql_mode, vous obtiendrez le message d'erreur suivant:

Le champ non groupé «Montant» est utilisé dans la clause HAVING: SELECT YEAR (date de commande), COUNT (*) comme montant FROM Orders GROUP BY YEAR (date de commande) HAVING Amount> 1

Voici la démo SQLFiddle

C'est donc à vous de configurer et d'utiliser votre instance de MySQL.

peterm
la source
Vous avez absolument raison sur la documentation. Je n'ai jamais pensé que cela pourrait être aussi clairement écrit que vous l'avez cité ci-dessus :) Merci de l'avoir trouvé ...
Ohlin
Cette réponse ne répond pas "MySQL effectue-t-il une pré-analyse ou MySQL utilise-t-il une interprétation conceptuelle différente?".
Pacerier
2
@Pacerier MySQL "effectue une pré-analyse", bien sûr, car l'optimiseur de requête prend en compte toutes les facettes de la requête tout en choisissant ce qu'il pense être le meilleur plan de requête. La notion d'une "interprétation conceptuelle différente" trahit une incompréhension du fait que le serveur est libre de mettre en œuvre le modèle conceptuel de quelque manière que ce soit qui produise un résultat valide. ORDER BY, par exemple, peut en fait être gérée bien plus tôt qu'il ne l'est théoriquement, si l'optimiseur constate que les lignes peuvent être initialement lues dans l'ordre à partir d'un index qui est déjà dans l'ordre souhaité.
Michael - sqlbot
4

Bonne question.

Je pense que vous devriez exécuter ces requêtes

EXPLAIN SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;
SHOW WARNINGS;

et vérifiez comment la requête est réécrite. je suis sûr que l'optimiseur de requête remplace le montant par COUNT (*)

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING COUNT(*)>1;

Comme avec

select 
 *
from 
 test
where 
 id = 5 - 3

après optimiseur de requête son quelque chose comme ça.

select 
 test.id as 'id'
from 
 test
where 
 test.id = 2
Raymond Nijland
la source