L'utilisation de l'alias de colonne dans la clause WHERE de la requête MySQL génère une erreur

202

La requête que j'exécute est la suivante, mais j'obtiens cette erreur:

# 1054 - Colonne inconnue 'Guarante_postcode' dans 'IN / ALL / ANY subquery'

SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE `guaranteed_postcode` NOT IN #this is where the fake col is being used
(
 SELECT `postcode` FROM `postcodes` WHERE `region` IN
 (
  'australia'
 )
)

Ma question est: pourquoi ne puis-je pas utiliser une fausse colonne dans la clause where de la même requête DB?

James
la source

Réponses:

434

Vous ne pouvez utiliser que des alias de colonne dans les clauses GROUP BY, ORDER BY ou HAVING.

SQL standard ne vous permet pas de faire référence à un alias de colonne dans une clause WHERE. Cette restriction est imposée car lorsque le code WHERE est exécuté, la valeur de la colonne peut ne pas encore être déterminée.

Copié à partir de la documentation MySQL

Comme indiqué dans les commentaires, utiliser HAVING à la place peut faire le travail. Assurez-vous de lire à ce WHERE vs HAVING cependant.

Victor Hugo
la source
1
Vive la réponse rapide et précise! J'ai jeté un œil à la clause HAVING et trouvé un moyen de réussir cette requête. Merci encore.
James
39
Dans le cas où quelqu'un d'autre a le même problème que moi qui utilisait le col aliasé dans une clause where échouant - le remplacement de 'WHERE' par 'HAVING l'a corrigé immédiatement +1 bonne réponse.
megaSteve4
@ megaSteve4 J'ai eu le même problème! L'utilisation de "HAVING" l'a résolu en douceur. :)
Johan
9
Cela peut ou peut ne pas être important dans votre cas, mais HAVINGs'exécute plus lentement queWHERE
DT
1
La raison havingest que les valeurs des colonnes doivent être calculées au moment où vous accédez à la having. Ce n'est pas le cas where, comme indiqué ci-dessus.
Millie Smith
24

Comme l'a souligné Victor, le problème vient de l'alias. Cela peut être évité cependant, en plaçant l'expression directement dans la clause WHERE x IN y:

SELECT `users`.`first_name`,`users`.`last_name`,`users`.`email`,SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE SUBSTRING(`locations`.`raw`,-6,4) NOT IN #this is where the fake col is being used
(
 SELECT `postcode` FROM `postcodes` WHERE `region` IN
 (
  'australia'
 )
)

Cependant, je suppose que cela est très inefficace, car la sous-requête doit être exécutée pour chaque ligne de la requête externe.

rodion
la source
1
@rodion, Oui, je pense que c'est très lent et inefficace.
Pacerier
20

SQL standard (ou MySQL) n'autorise pas l'utilisation d'alias de colonne dans une clause WHERE car

lorsque la clause WHERE est évaluée, la valeur de la colonne n'a peut-être pas encore été déterminée.

(à partir de la documentation MySQL ). Ce que vous pouvez faire est de calculer la valeur de la colonne dans la clause WHERE , d'enregistrer la valeur dans une variable et de l'utiliser dans la liste des champs. Par exemple, vous pouvez faire ceci:

SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
@postcode AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE (@postcode := SUBSTRING(`locations`.`raw`,-6,4)) NOT IN
(
 SELECT `postcode` FROM `postcodes` WHERE `region` IN
 (
  'australia'
 )
)

Cela évite de répéter l'expression lorsqu'elle se complique, ce qui facilite la maintenance du code.

Joni
la source
9
Cela n'entre-t-il pas en conflit avec la documentation qui dit "En règle générale, vous ne devez jamais affecter une valeur à une variable utilisateur et lire la valeur dans la même instruction. Vous pourriez obtenir les résultats attendus, mais ce n'est pas garanti." ?
Arjan
C'est certainement quelque chose à garder à l'esprit. Cela a toujours fonctionné pour moi, je pense que l'ordre d'évaluation des différentes parties d'une déclaration a dû être fixé (d'abord WHERE, puis SELECT, puis GROUP BY, ...) mais je n'ai pas de référence pour cela
Joni
Quelques exemples: certains prétendent que pour eux select @code:=sum(2), 2*@codefonctionne dans MySQL 5.5, mais pour moi dans 5.6 la deuxième colonne donne NULL lors de la première invocation, et retourne 2 fois le résultat précédent lors d'une nouvelle exécution. Assez intéressant, les deux sélectionnent @code:=2, 2*@codeet select @code:=rand(), 2*@codesemblent fonctionner dans ma version 5.6 (aujourd'hui). Mais ceux-ci sont en effet en train d'écrire et de lire dans la clause SELECT; dans votre cas, vous le placez où.
Arjan
@Joni, pourquoi ne pas simplement évaluer la condition deux fois? Sûrement MySQL est assez intelligent pour optimiser cela .......
Pacerier
@Pacerier devoir répéter l'expression est encore pire, surtout si c'est compliqué. Je n'ai pas pu confirmer si MySQL implémente l'élimination de la sous-expression commune.
Joni
16

Ma réponse est peut-être trop tard, mais cela peut aider les autres.

Vous pouvez le joindre à une autre instruction select et utiliser la clause where.

SELECT * FROM (Select col1, col2,...) as t WHERE t.calcAlias > 0

calcAlias ​​est la colonne d'alias qui a été calculée.

George Khouri
la source
Agréable et court, mais c'est trop vague pour être utile.
Agamemnus
@Agamemnus, que voulez-vous dire par là?
Pacerier
La question était: "pourquoi ne puis-je pas utiliser une fausse colonne dans la clause where de la même requête DB?" Cette réponse ne répond pas à cette question et il manque un verbe.
Agamemnus le
Ensuite, utilisez simplement le HAVING
Hett
8

Vous pouvez utiliser la clause HAVING pour le filtre calculé dans les champs SELECT et les alias

Hett
la source
@ fahimg23 - Pas sûr. J'ai essayé de trouver une raison, mais je ne peux pas! Gardez à l'esprit les différences entre WHEREet HAVINGcependant. Ils ne sont pas identiques. stackoverflow.com/search?q=where+vs+having
rinogo
MISE À JOUR: C'est parce que cette réponse fournit la même solution mais avec plus d'informations.
rinogo
1

J'utilise mysql 5.5.24 et le code suivant fonctionne:

select * from (
SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
) as a
WHERE guaranteed_postcode NOT IN --this is where the fake col is being used
(
 SELECT `postcode` FROM `postcodes` WHERE `region` IN
 (
  'australia'
 )
)
themhz
la source
0

SQL standard interdit les références aux alias de colonne dans une clause WHERE. Cette restriction est imposée car lorsque la clause WHERE est évaluée, la valeur de la colonne n'a peut-être pas encore été déterminée. Par exemple, la requête suivante est illégale:

SELECT id, COUNT (*) AS cnt FROM tbl_name WHERE cnt> 0 GROUP BY id;

Pavan Rajput
la source
0

Vous pouvez utiliser SUBSTRING ( locations. raw, -6,4) pour savoir où conditon

SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE SUBSTRING(`locations`.`raw`,-6,4) NOT IN #this is where the fake col is being used
(
SELECT `postcode` FROM `postcodes` WHERE `region` IN
(
 'australia'
)
)
Sameera Prasad Jayasinghe
la source