Usar o alias da coluna na cláusula WHERE da consulta MySQL produz um erro

202

A consulta que estou executando é a seguinte, porém estou recebendo este erro:

# 1054 - Coluna desconhecida 'Guarante_postcode' na 'Subconsulta IN / ALL / ANY'

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

Minha pergunta é: por que não consigo usar uma coluna falsa na cláusula where da mesma consulta ao banco de dados?

James
fonte

Respostas:

434

Você só pode usar aliases de coluna nas cláusulas GROUP BY, ORDER BY ou HAVING.

O SQL padrão não permite que você consulte um alias de coluna em uma cláusula WHERE. Essa restrição é imposta porque quando o código WHERE é executado, o valor da coluna ainda não pode ser determinado.

Copiado da documentação do MySQL

Conforme apontado nos comentários, usar HAVING pode fazer o trabalho. Certifique-se de dar uma leitura neste WHERE vs HAVING embora.

Victor Hugo
fonte
1
Felicidades pela resposta rápida e precisa! Examinei a cláusula HAVING e descobri uma maneira de executar essa consulta com êxito. Obrigado novamente.
James
39
No caso de alguém ter o mesmo problema que eu, que estava usando a coluna alias em uma cláusula where falhando - a troca de 'WHERE' por 'HAVING corrigiu imediatamente +1 uma boa resposta.
MegaSteve4 28/05
@ megaSteve4 Eu tive o mesmo problema! Usando "HAVING" resolveu-o sem problemas. :)
Johan
9
Isso pode ou não ser importante no seu caso, mas HAVINGexecuta mais lenta do queWHERE
DTs
1
O motivo havingfunciona é porque os valores da coluna precisam ser calculados quando você chegar ao having. Este não é o caso where, como afirmado acima.
Millie Smith
24

Como Victor apontou, o problema está no alias. Isso pode ser evitado, colocando a expressão diretamente na 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'
 )
)

No entanto, acho que isso é muito ineficiente, pois a subconsulta deve ser executada para todas as linhas da consulta externa.

rodion
fonte
1
@ rodion, Sim, eu acredito que isso é severamente lento e ineficiente.
Pacerier 9/04
20

O SQL padrão (ou MySQL) não permite o uso de aliases de coluna em uma cláusula WHERE porque

quando a cláusula WHERE é avaliada, o valor da coluna ainda pode não ter sido determinado.

(da documentação do MySQL ). O que você pode fazer é calcular o valor da coluna na cláusula WHERE , salvar o valor em uma variável e usá-lo na lista de campos. Por exemplo, você pode fazer isso:

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

Isso evita repetir a expressão quando ela se torna complicada, facilitando a manutenção do código.

Joni
fonte
9
Isso não entra em conflito com a documentação que diz "Como regra geral, você nunca deve atribuir um valor a uma variável de usuário e ler o valor na mesma instrução. Você pode obter os resultados esperados, mas isso não é garantido". ?
Arjan #
Definitivamente, é algo para se ter em mente. Porém, sempre funcionou para mim, acho que a ordem de avaliação das diferentes partes de uma declaração teve que ser corrigida (primeiro WHERE, depois SELECT, depois GROUP BY, ...), mas não tenho uma referência para isso.
Joni
Alguns exemplos: alguns afirmam que para eles select @code:=sum(2), 2*@codefuncionam no MySQL 5.5, mas para mim na 5.6 a segunda coluna gera NULL na primeira chamada e retorna 2 vezes o resultado anterior quando é executada novamente. Interessante o suficiente, ambos selecionam @code:=2, 2*@codee select @code:=rand(), 2*@codeparecem funcionar no meu 5.6 (hoje). Mas esses estão realmente escrevendo e lendo na cláusula SELECT; no seu caso, você está definindo em WHERE.
Arjan #
@ Joni, por que não apenas avaliar a condição duas vezes? Certamente MySQL é suficiente inteligente para otimizar o .......
Pacerier
O @Pacerier ter que repetir a expressão ainda é pior, especialmente se for complicado. Não consegui confirmar se o MySQL implementa a eliminação de subexpressão comum.
Joni
16

Talvez minha resposta seja tarde demais, mas isso possa ajudar outras pessoas.

Você pode anexá-lo a outra instrução select e usar a cláusula where.

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

calcAlias ​​é a coluna de alias que foi calculada.

George Khouri
fonte
Bom e curto, mas isso é muito vago para ser útil.
Agamemnus 31/12/14
@ Agamemnus, o que você quer dizer com isso?
Pacerier 9/04
A pergunta era: "por que não consigo usar uma coluna falsa na cláusula where da mesma consulta ao banco de dados?" Esta resposta não responde a essa pergunta e está faltando um verbo.
Agamemnus
Em seguida, basta usar o HAVING
Hett
8

Você pode usar a cláusula HAVING para o filtro calculado nos campos e aliases SELECT

Hett
fonte
@ fahimg23 - Não tenho certeza. Tentei encontrar uma razão para isso, mas não posso! Lembre-se das diferenças entre WHEREe HAVING, no entanto. Eles não são idênticos. stackoverflow.com/search?q=where+vs+having
rinogo
ATUALIZAÇÃO: É porque esta resposta fornece a mesma solução, mas com mais informações.
Rinogo
1

Estou usando o mysql 5.5.24 e o seguinte 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
fonte
0

O SQL padrão não permite referências a aliases de coluna em uma cláusula WHERE. Essa restrição é imposta porque, quando a cláusula WHERE é avaliada, o valor da coluna ainda pode não ter sido determinado. Por exemplo, a seguinte consulta é ilegal:

SELECT id, COUNT (*) AS cnt FROM nome_tabela ONDE cnt> 0 GROUP BY id;

Pavan Rajput
fonte
0

Você pode usar SUBSTRING ( locations. raw, -6,4) para saber onde

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
fonte