Die Verwendung eines Spaltenalias in der WHERE-Klausel der MySQL-Abfrage führt zu einem Fehler

199

Die Abfrage, die ich ausführe, lautet wie folgt, es wird jedoch folgende Fehlermeldung angezeigt:

# 1054 - Unbekannte Spalte 'garantierter Postleitzahl' in 'IN / ALL / ANY-Unterabfrage'

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

Meine Frage lautet: Warum kann ich keine gefälschte Spalte in der where-Klausel derselben DB-Abfrage verwenden?

James
quelle

Antworten:

432

Sie können Spaltenaliasnamen nur in den Klauseln GROUP BY, ORDER BY oder HAVING verwenden.

Mit Standard-SQL können Sie nicht auf einen Spaltenalias in einer WHERE-Klausel verweisen. Diese Einschränkung wird auferlegt, da bei Ausführung des WHERE-Codes der Spaltenwert möglicherweise noch nicht ermittelt wurde.

Aus der MySQL-Dokumentation kopiert

Wie in den Kommentaren erwähnt, kann die Verwendung von HAVING die Arbeit erledigen. Lesen Sie unbedingt WHERE vs HAVING .

Victor Hugo
quelle
1
Prost auf die schnelle und genaue Antwort! Ich habe mir die HAVING-Klausel angesehen und einen Weg gefunden, um diese Abfrage erfolgreich auszuführen. Danke noch einmal.
James
38
Für den Fall, dass jemand anderes das gleiche Problem wie ich hat, das die Alias-Spalte in einer fehlgeschlagenen where-Klausel verwendet hat - das Austauschen von 'WHERE' gegen 'HAVING' wurde sofort behoben +1 gute Antwort.
megaSteve4
@ MegaSteve4 Ich hatte das gleiche Problem! Mit "HAVING" wurde es reibungslos gelöst. :) :)
Johan
9
Dies kann in Ihrem Fall wichtig sein oder auch nicht, wird jedoch HAVINGlangsamer ausgeführt alsWHERE
DTs
1
Der Grund dafür havingist, dass die Spaltenwerte bis zum Erreichen des berechnet werden müssen having. Dies ist nicht der Fall where, wie oben angegeben.
Millie Smith
24

Wie Victor betonte, liegt das Problem beim Alias. Dies kann jedoch vermieden werden, indem der Ausdruck direkt in die WHERE x IN y-Klausel eingefügt wird:

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

Ich denke jedoch, dass dies sehr ineffizient ist, da die Unterabfrage für jede Zeile der äußeren Abfrage ausgeführt werden muss.

rodion
quelle
1
@rodion, ja ich glaube das ist sehr langsam und ineffizient.
Pacerier
20

Standard SQL (oder MySQL) erlaubt die Verwendung von Spaltenaliasnamen in einer WHERE-Klausel nicht, weil

Wenn die WHERE-Klausel ausgewertet wird, wurde der Spaltenwert möglicherweise noch nicht ermittelt.

(aus der MySQL-Dokumentation ). Sie können den Spaltenwert in der WHERE- Klausel berechnen , den Wert in einer Variablen speichern und in der Feldliste verwenden. Zum Beispiel könnten Sie dies tun:

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

Dadurch wird vermieden, dass der Ausdruck wiederholt wird, wenn er kompliziert wird, wodurch der Code einfacher zu warten ist.

Joni
quelle
9
Widerspricht dies nicht der Dokumentation , in der steht: "In der Regel sollten Sie einer Benutzervariablen niemals einen Wert zuweisen und den Wert in derselben Anweisung lesen. Möglicherweise erhalten Sie die erwarteten Ergebnisse, dies ist jedoch nicht garantiert." ?
Arjan
Das ist definitiv etwas zu beachten. Es hat zwar immer für mich funktioniert, ich denke, die Reihenfolge der Bewertung der verschiedenen Teile einer Aussage musste festgelegt werden (zuerst WO, dann AUSWÄHLEN, dann GRUPPEN NACH, ...), aber ich habe keine Referenz dafür
Joni
Einige Beispiele: Einige behaupten, dass für sie select @code:=sum(2), 2*@codein MySQL 5.5 funktioniert, aber für mich in 5.6 ergibt die zweite Spalte beim ersten Aufruf NULL und gibt beim erneuten Ausführen das Zweifache des vorherigen Ergebnisses zurück. Interessant genug, beide wählen @code:=2, 2*@codeund select @code:=rand(), 2*@codescheinen in meinem 5.6 (heute) zu funktionieren. Aber diese schreiben und lesen tatsächlich in der SELECT-Klausel; In Ihrem Fall setzen Sie es in WHERE.
Arjan
@Joni, warum nicht einfach den Zustand zweimal bewerten? Sicherlich ist MySQL klug genug, um das zu optimieren .......
Pacerier
@ Pacerier den Ausdruck wiederholen zu müssen ist noch schlimmer, besonders wenn es kompliziert ist. Ich konnte nicht bestätigen, ob MySQL die gemeinsame Eliminierung von Unterausdrücken implementiert.
Joni
16

Vielleicht ist meine Antwort zu spät, aber das kann anderen helfen.

Sie können es einer anderen select-Anweisung hinzufügen und die where-Klausel verwenden.

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

calcAlias ​​ist die Alias-Spalte, die berechnet wurde.

George Khouri
quelle
Schön und kurz, aber das ist zu vage, um nützlich zu sein.
Agamemnus
@Agamemnus, was meinst du damit?
Pacerier
Die Frage war: "Warum kann ich keine gefälschte Spalte in der where-Klausel derselben DB-Abfrage verwenden?" Diese Antwort beantwortet diese Frage nicht und es fehlt ein Verb.
Agamemnus
Dann benutzen Sie einfach das HAVING
Hett
8

Sie können die HAVING-Klausel für Filter verwenden, die in SELECT-Feldern und Aliasen berechnet werden

Hett
quelle
@ fahimg23 - Nicht sicher. Ich habe versucht, einen Grund dafür zu finden, aber ich kann nicht! Beachten Sie jedoch die Unterschiede zwischen WHEREund HAVING. Sie sind nicht identisch. stackoverflow.com/search?q=where+vs+having
rinogo
UPDATE: Das liegt daran, dass diese Antwort dieselbe Lösung bietet, aber mehr Informationen enthält.
Rinogo
1

Ich verwende MySQL 5.5.24 und der folgende Code funktioniert:

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
quelle
0

Standard-SQL erlaubt keine Verweise auf Spaltenaliasnamen in einer WHERE-Klausel. Diese Einschränkung wird auferlegt, da bei der Auswertung der WHERE-Klausel der Spaltenwert möglicherweise noch nicht ermittelt wurde. Die folgende Abfrage ist beispielsweise unzulässig:

SELECT id, COUNT (*) AS cnt FROM tbl_name WHERE cnt> 0 GROUP BY id;

Pavan Rajput
quelle
0

Sie können SUBSTRING ( locations. raw, -6,4) für die Bedingung verwenden

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
quelle