Użycie aliasu kolumny w klauzuli WHERE zapytania MySQL powoduje błąd

201

Uruchamiane przeze mnie zapytanie jest następujące, jednak pojawia się ten błąd:

# 1054 - Nieznana kolumna „gwarantowany kod pocztowy” w „podzapytaniu 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'
 )
)

Moje pytanie brzmi: dlaczego nie mogę użyć fałszywej kolumny w klauzuli where tego samego zapytania DB?

James
źródło

Odpowiedzi:

434

Aliasów kolumn można używać tylko w klauzulach GROUP BY, ORDER BY lub HAVING.

Standardowy SQL nie pozwala na odwoływanie się do aliasu kolumny w klauzuli WHERE. To ograniczenie jest nałożone, ponieważ podczas wykonywania kodu GDZIE wartość kolumny może nie zostać jeszcze określona.

Skopiowano z dokumentacji MySQL

Jak wskazano w komentarzach, użycie HAVING zamiast tego może wykonać pracę. Pamiętaj jednak, aby przeczytać w tym miejscu GDZIE vs POSIADANIE .

Wiktor Hugo
źródło
1
Pozdrawiam za szybką i dokładną odpowiedź! Przejrzałem klauzulę HAVING i opracowałem sposób na pomyślne uruchomienie tego zapytania. Dzięki jeszcze raz.
James
38
W przypadku, gdy ktoś inny ma taki sam problem jak ja, który używał aliasu col w klauzuli where, która zawodzi - zamieniając „GDZIE” na „POSIADANIE” naprawiono go od razu +1 dobra odpowiedź.
megaSteve4
@ megaSteve4 Miałem ten sam problem! Korzystanie z „HAVING” rozwiązało to płynnie. :)
Johan
9
To może, ale nie musi być ważne w twoim przypadku, ale HAVINGdziała wolniej niżWHERE
DT
1
Powodem havingjest to, że wartości kolumn muszą być obliczone do czasu dotarcia do having. Nie jest tak w przypadku where, jak stwierdzono powyżej.
Millie Smith,
24

Jak zauważył Victor, problem dotyczy aliasu. Można tego jednak uniknąć, umieszczając wyrażenie bezpośrednio w klauzuli 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'
 )
)

Wydaje mi się jednak, że jest to bardzo nieefektywne, ponieważ podzapytanie należy wykonać dla każdego wiersza zewnętrznego zapytania.

rodion
źródło
1
@rodion, Tak, uważam, że jest to bardzo powolne i nieefektywne.
Pacerier
20

Standardowy SQL (lub MySQL) nie zezwala na użycie aliasów kolumn w klauzuli WHERE, ponieważ

przy ocenie klauzuli WHERE wartość kolumny mogła jeszcze nie zostać określona.

(z dokumentacji MySQL ). Możesz obliczyć wartość kolumny w klauzuli WHERE , zapisać wartość w zmiennej i użyć jej na liście pól. Na przykład możesz to zrobić:

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

Pozwala to uniknąć powtarzania wyrażenia, gdy staje się skomplikowane, co ułatwia utrzymanie kodu.

Joni
źródło
9
Czy nie koliduje to z dokumentacją, która mówi: „Zasadniczo nigdy nie należy przypisywać wartości do zmiennej użytkownika i odczytywać wartości w ramach tej samej instrukcji. Możesz uzyskać oczekiwane wyniki, ale nie jest to gwarantowane”. ?
Arjan
To zdecydowanie coś, o czym należy pamiętać. Zawsze jednak działało to dla mnie, myślę, że kolejność oceny różnych części instrukcji musiała zostać naprawiona (najpierw GDZIE, potem WYBIERZ, a następnie GRUPUJ WEDŁUG ...), ale nie mam na to odniesienia
Joni
Kilka przykładów: niektóre twierdzą, że dla nich select @code:=sum(2), 2*@codedziała w MySQL 5.5, ale dla mnie w 5.6 druga kolumna zwraca NULL przy pierwszym wywołaniu i zwraca 2 razy poprzedni wynik po ponownym uruchomieniu. Interesujące jest to, że zarówno select, jak @code:=2, 2*@codei select @code:=rand(), 2*@codewydają się działać w moim 5.6 (dziś). Ale rzeczywiście piszą i czytają w klauzuli SELECT; w twoim przypadku ustawiasz to w GDZIE.
Arjan
@Joni, Dlaczego nie po prostu ocenić stan dwa razy? Z pewnością MySQL jest wystarczająco inteligentny, aby zoptymalizować to .......
Pacerier,
@Pacerier musi powtórzyć wyrażenie jest jeszcze gorsze, szczególnie jeśli jest skomplikowane. Nie byłem w stanie potwierdzić, czy MySQL implementuje powszechną eliminację podwyrażeń.
Joni
16

Może moja odpowiedź jest za późna, ale może to pomóc innym.

Możesz dołączyć go do innej instrukcji select i użyć klauzuli where.

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

calcAlias ​​to obliczona kolumna aliasowa.

George Khouri
źródło
Ładne i krótkie, ale jest to zbyt niejasne, aby było przydatne.
Agamemnus,
@Agamemnus, co przez to rozumiesz?
Pacerier
Pytanie brzmiało: „Dlaczego nie mogę użyć fałszywej kolumny w klauzuli where tego samego zapytania DB?” Ta odpowiedź nie odpowiada na to pytanie i brakuje czasownika.
Agamemnus,
Następnie skorzystaj z HAVING
Hett
8

Możesz użyć klauzuli HAVING dla filtra obliczonego w polach SELECT i aliasach

Hett
źródło
@ fahimg23 - Nie jestem pewien. Próbowałem znaleźć powód, ale nie mogę! Pamiętaj jednak o różnicach między WHEREi HAVING. Nie są identyczni. stackoverflow.com/search?q=where+vs+having
rinogo
AKTUALIZACJA: To dlatego, że ta odpowiedź zapewnia to samo rozwiązanie, ale zawiera więcej informacji.
rinogo
1

Korzystam z mysql 5.5.24 i działa następujący kod:

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
źródło
0

Standardowy SQL nie zezwala na odwołania do aliasów kolumn w klauzuli WHERE. To ograniczenie jest nałożone, ponieważ podczas oceny klauzuli WHERE wartość kolumny mogła nie zostać jeszcze określona. Na przykład następujące zapytanie jest nielegalne:

WYBIERZ id, COUNT (*) AS cnt OD tbl_name GDZIE cnt> 0 GROUP BY id;

Pavan Rajput
źródło
0

Możesz użyć SUBSTRING ( locations. raw, -6,4), aby ustalić gdzie warunek

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
źródło