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?

James
fuente

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 .

Victor Hugo
fuente
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.

rodion
fuente
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.

Joni
fuente
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ó.

George Khouri
fuente
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

Hett
fuente
@ 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'
 )
)
themhz
fuente
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;

Pavan Rajput
fuente
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'
)
)
Sameera Prasad Jayasinghe
fuente