El uso de alias de columna en la cláusula WHERE de la consulta MySQL produce un error


202

La consulta que estoy ejecutando es la siguiente, sin embargo, recibo este error:

# 1054 - Columna desconocida 'garantizado_código postal' en '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'
 )
)

Mi pregunta es: ¿por qué no puedo usar una columna falsa en la cláusula where de la misma consulta DB?

Respuestas:


434

Solo puede usar alias de columna en las cláusulas GROUP BY, ORDER BY o HAVING.

SQL estándar no le permite hacer referencia a un alias de columna en una cláusula WHERE. Esta restricción se impone porque cuando se ejecuta el código WHERE, el valor de la columna aún no se puede determinar.

Copiado de la documentación de MySQL

Como se señaló en los comentarios, usar HAVING en su lugar puede hacer el trabajo. Sin embargo, asegúrate de leer esto en DONDE vs TENER .


1
¡Salud por la respuesta rápida y precisa! He echado un vistazo a la cláusula HAVING y he encontrado una forma de ejecutar con éxito esta consulta. Gracias de nuevo.
James

39
En caso de que alguien más tenga el mismo problema que yo, que estaba usando la columna con alias en una cláusula where que falla, intercambiando el 'DÓNDE' por 'HABERLO solucionado de inmediato +1 buena respuesta.
megaSteve4

@ megaSteve4 ¡Tenía el mismo problema! Usando "HAVING" lo resolvió sin problemas. :)
Johan

9
Esto puede o no ser importante en su caso, pero se HAVINGejecuta más lento queWHERE
DT

1
La razón havingfunciona porque los valores de las columnas deben calcularse antes de llegar al having. Este no es el caso where, como se indicó anteriormente.
Millie Smith

24

Como señaló Víctor, el problema es con el alias. Sin embargo, esto se puede evitar colocando la expresión directamente en la cláusula 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'
 )
)

Sin embargo, supongo que esto es muy ineficiente, ya que la subconsulta debe ejecutarse para cada fila de la consulta externa.


1
@rodion, sí, creo que esto es muy lento e ineficiente.
Pacerier

20

SQL estándar (o MySQL) no permite el uso de alias de columna en una cláusula WHERE porque

cuando se evalúa la cláusula WHERE, es posible que el valor de la columna aún no se haya determinado.

(de la documentación de MySQL ). Lo que puede hacer es calcular el valor de la columna en la cláusula WHERE , guardar el valor en una variable y usarlo en la lista de campos. Por ejemplo, podrías hacer esto:

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

Esto evita repetir la expresión cuando se complica, lo que hace que el código sea más fácil de mantener.


9
Esto no entra en conflicto con la documentación que dice "Como regla general, nunca debe asignar un valor a una variable de usuario y leer el valor dentro de la misma declaración. Puede obtener los resultados que espera, pero esto no está garantizado". ?
Arjan

Eso es definitivamente algo a tener en cuenta. Sin embargo, siempre me ha funcionado, creo que el orden de evaluación de las diferentes partes de una declaración tuvo que ser arreglado (primero DONDE, luego SELECCIONAR, luego GRUPO POR ...) pero no tengo una referencia para eso
Joni

Algunos ejemplos: algunos afirman que para ellos select @code:=sum(2), 2*@codefunciona en MySQL 5.5, pero para mí en 5.6 la segunda columna arroja NULL en la primera invocación, y devuelve 2 veces el resultado anterior cuando se ejecuta nuevamente. Lo suficientemente interesante, tanto seleccionar @code:=2, 2*@codecomo select @code:=rand(), 2*@codehacer funcionar en mi 5.6 (hoy). Pero esos de hecho están escribiendo y leyendo en la cláusula SELECT; en su caso lo está configurando en DONDE.
Arjan

@Joni, ¿por qué no solo evaluar la condición dos veces? Seguramente MySQL es lo suficientemente inteligente como para optimizar eso .......
Pacerier

@Pacerier tener que repetir la expresión es aún peor, especialmente si es complicado. No he podido confirmar si MySQL implementa la eliminación de subexpresión común.
Joni

16

Tal vez mi respuesta es demasiado tarde, pero esto puede ayudar a otros.

Puede encerrarlo con otra instrucción select y usar la cláusula where.

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

calcAlias ​​es la columna de alias que se calculó.


Agradable y breve, pero esto es demasiado vago para ser útil.
Agamemnus

@Agamemnus, ¿qué quieres decir con eso?
Pacerier

La pregunta era: "¿por qué no puedo usar una columna falsa en la cláusula where de la misma consulta DB?" Esta respuesta no responde esa pregunta y le falta un verbo.
Agamemnus

Luego simplemente use el HAVING
Hett

8

Puede usar la cláusula HAVING para el filtro calculado en SELECCIONAR campos y alias


@ fahimg23 - No estoy seguro. Traté de encontrar una razón, ¡pero no puedo! Sin embargo, tenga en cuenta las diferencias entre WHEREy HAVING. No son idénticos stackoverflow.com/search?q=where+vs+having
rinogo

ACTUALIZACIÓN: es porque esta respuesta proporciona la misma solución pero con más información.
rinogo

1

Estoy usando mysql 5.5.24 y el siguiente código funciona:

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

0

SQL estándar no permite referencias a alias de columna en una cláusula WHERE. Esta restricción se impone porque cuando se evalúa la cláusula WHERE, el valor de la columna puede no haberse determinado todavía. Por ejemplo, la siguiente consulta es ilegal:

SELECCIONE id, COUNT (*) COMO cnt FROM tbl_name DONDE cnt> 0 GROUP BY id;


0

Puede usar SUBSTRING ( locations. raw, -6,4) para donde la condición

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'
)
)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.