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?

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.


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.


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.


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.


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


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

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;


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'
)
)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.