L'uso dell'alias di colonna nella clausola WHERE della query MySQL produce un errore


202

La query che sto eseguendo è la seguente, tuttavia visualizzo questo errore:

# 1054 - Colonna sconosciuta 'garantito_postcode' in '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'
 )
)

La mia domanda è: perché non riesco a utilizzare una colonna falsa nella clausola where della stessa query DB?

Risposte:


434

Puoi utilizzare solo gli alias di colonna nelle clausole GROUP BY, ORDER BY o HAVING.

SQL standard non consente di fare riferimento a un alias di colonna in una clausola WHERE. Questa restrizione è imposta perché quando viene eseguito il codice WHERE, il valore della colonna potrebbe non essere ancora determinato.

Copiato dalla documentazione di MySQL

Come sottolineato nei commenti, l'utilizzo di HAVING invece può fare il lavoro. Assicurati di dare una lettura a questo WHERE vs HAVING però.


1
Saluti per la risposta rapida e accurata! Ho dato un'occhiata alla clausola HAVING e ho trovato un modo per eseguire correttamente questa query. Grazie ancora.
James,

39
Nel caso in cui qualcun altro abbia la stessa mia probabilita 'che stava usando il filtro alias in una clausola where in cui non funzionava - scambiando' WHERE 'con' HAVING l'ha risolto immediatamente +1 buona risposta.
megaSteve4,

@ megaSteve4 Ho avuto lo stesso problema! Usando "HAVING" lo hai risolto senza problemi. :)
Johan,

9
Questo può essere o non essere importante nel tuo caso, ma HAVINGviene eseguito più lentamente diWHERE
DT

1
Il motivo havingfunziona perché i valori della colonna devono essere calcolati quando si arriva a having. Questo non è il caso di where, come affermato sopra.
Millie Smith,

24

Come ha sottolineato Victor, il problema è con l'alias. Questo può essere evitato però, inserendo l'espressione direttamente nella clausola 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'
 )
)

Tuttavia, suppongo che questo sia molto inefficiente, poiché la sottoquery deve essere eseguita per ogni riga della query esterna.


1
@rodion, Sì, credo che questo sia molto lento e inefficiente.
Pacerier,

20

SQL standard (o MySQL) non consente l'utilizzo di alias di colonna in una clausola WHERE perché

quando viene valutata la clausola WHERE, il valore della colonna potrebbe non essere stato ancora determinato.

(dalla documentazione di MySQL ). Quello che puoi fare è calcolare il valore della colonna nella clausola WHERE , salvare il valore in una variabile e usarlo nell'elenco dei campi. Ad esempio potresti farlo:

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

Ciò evita di ripetere l'espressione quando diventa complicata, facilitando la manutenzione del codice.


9
Non è in conflitto con la documentazione che dice "Come regola generale, non si dovrebbe mai assegnare un valore a una variabile utente e leggere il valore all'interno della stessa istruzione. È possibile ottenere i risultati previsti, ma ciò non è garantito." ?
Arjan,

Questo è sicuramente qualcosa da tenere a mente. Per me ha sempre funzionato, penso che l'ordine di valutazione delle diverse parti di un'istruzione debba essere corretto (prima DOVE, poi SELEZIONA, poi GRUPPO PER, ...) ma non ho un riferimento per questo
Joni,

Alcuni esempi: alcuni sostengono che per loro select @code:=sum(2), 2*@codefunziona in MySQL 5.5, ma per me in 5.6 la seconda colonna restituisce NULL alla prima invocazione e restituisce 2 volte il risultato precedente quando viene eseguita nuovamente. Abbastanza interessante, entrambi selezionano @code:=2, 2*@codee select @code:=rand(), 2*@codesembrano funzionare nel mio 5.6 (oggi). Ma quelli stanno davvero scrivendo e leggendo nella clausola SELECT; nel tuo caso lo stai impostando in WHERE.
Arjan,

@Joni, Perché non valutare due volte la condizione? Sicuramente MySQL è abbastanza intelligente da ottimizzare questo .......
Pacerier

@Pacerier dover ripetere l'espressione è ancora peggio soprattutto se è complicato. Non sono stato in grado di confermare se MySQL implementa l'eliminazione della sottoespressione comune.
Joni,

16

Forse la mia risposta è troppo tardi, ma questo può aiutare gli altri.

È possibile racchiuderlo con un'altra istruzione select e utilizzare la clausola where.

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

calcAlias ​​è la colonna alias che è stata calcolata.


Bello e breve, ma questo è troppo vago per essere utile.
Agamemnus,

@Agamemnus, cosa intendi con questo?
Pacerier,

La domanda era: "perché non riesco a utilizzare una colonna falsa nella clausola where della stessa query DB?" Questa risposta non risponde a quella domanda e manca un verbo.
Agamemnus,

Quindi usa semplicemente HAVING
Hett il

8

È possibile utilizzare la clausola HAVING per il filtro calcolato nei campi e negli alias SELECT


@ fahimg23 - Non sono sicuro. Ho provato a trovare un motivo, ma non ci riesco! Tieni presente le differenze tra WHEREe HAVING, comunque. Non sono identici stackoverflow.com/search?q=where+vs+having
rinogo

AGGIORNAMENTO: è perché questa risposta fornisce la stessa soluzione ma con maggiori informazioni.
Rinogo,

1

Sto usando mysql 5.5.24 e il seguente codice funziona:

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 standard non consente riferimenti ad alias di colonna in una clausola WHERE. Questa restrizione è imposta perché, quando viene valutata la clausola WHERE, il valore della colonna potrebbe non essere stato ancora determinato. Ad esempio, la seguente query è illegale:

SELEZIONA id, COUNT (*) COME cnt DA tbl_name DOVE cnt> 0 GROUP BY id;


0

È possibile utilizzare SUBSTRING ( locations. raw, -6,4) per dove 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'
)
)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.