Menggunakan alias kolom dalam klausa WHERE dari permintaan MySQL menghasilkan kesalahan

202

Kueri yang saya jalankan adalah sebagai berikut, namun saya mendapatkan kesalahan ini:

# 1054 - Kolom tidak dikenal 'Guarant_postcode' di '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'
 )
)

Pertanyaan saya adalah: mengapa saya tidak dapat menggunakan kolom palsu di klausa mana dari permintaan DB yang sama?

James
sumber

Jawaban:

434

Anda hanya dapat menggunakan alias kolom dalam klausa GROUP BY, ORDER BY, atau HAVING.

SQL standar tidak memungkinkan Anda untuk merujuk ke alias kolom di klausa WHERE. Pembatasan ini diberlakukan karena ketika kode WHERE dijalankan, nilai kolom mungkin belum ditentukan.

Disalin dari dokumentasi MySQL

Seperti yang ditunjukkan dalam komentar, menggunakan HAVING sebagai gantinya dapat melakukan pekerjaan. Pastikan untuk membaca di MANA ini vs HAVING sekalipun.

victor hugo
sumber
1
Bersorak untuk respon cepat dan akurat! Saya telah melihat klausa HAVING dan mencari cara untuk berhasil menjalankan kueri ini. Terima kasih lagi.
James
39
Jika ada orang lain yang memiliki masalah yang sama seperti saya yang menggunakan col alias di mana klausa gagal - bertukar 'WHERE' untuk 'HAVING memperbaikinya langsung +1 jawaban yang baik.
megaSteve4
@ megaSteve4 Saya memang memiliki masalah yang sama! Menggunakan "HAVING" menyelesaikannya dengan lancar. :)
Johan
9
Ini mungkin atau mungkin tidak penting dalam kasus Anda, tetapi HAVINGmengeksekusi lebih lambat dariWHERE
DTs
1
Alasannya havingadalah karena nilai kolom harus dihitung pada saat Anda mendapatkan having. Ini tidak terjadi dengan where, sebagaimana dinyatakan di atas.
Millie Smith
24

Seperti yang ditunjukkan Victor, masalahnya adalah pada alias. Ini bisa dihindari, dengan meletakkan ekspresi langsung ke klausa 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'
 )
)

Namun, saya kira ini sangat tidak efisien, karena subquery harus dieksekusi untuk setiap baris permintaan luar.

rodion
sumber
1
@rodion, Ya saya percaya ini sangat lambat dan tidak efisien.
Pacerier
20

SQL standar (atau MySQL) tidak mengizinkan penggunaan alias kolom dalam klausa WHERE karena

ketika klausa WHERE dievaluasi, nilai kolom mungkin belum ditentukan.

(dari dokumentasi MySQL ). Apa yang bisa Anda lakukan adalah menghitung nilai kolom dalam klausa WHERE , menyimpan nilai dalam variabel, dan menggunakannya dalam daftar bidang. Misalnya Anda bisa melakukan ini:

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

Ini menghindari pengulangan ekspresi ketika bertambah rumit, membuat kode lebih mudah untuk dipelihara.

Joni
sumber
9
Tidak bertentangan dengan dokumentasi yang mengatakan "Sebagai aturan umum, Anda tidak boleh menetapkan nilai untuk variabel pengguna dan membaca nilai dalam pernyataan yang sama. Anda mungkin mendapatkan hasil yang Anda harapkan, tetapi ini tidak dijamin." ?
Arjan
Itu pasti sesuatu yang perlu diingat. Namun selalu berhasil bagi saya, saya pikir urutan evaluasi dari bagian-bagian yang berbeda dari sebuah pernyataan harus diperbaiki (pertama DI MANA, lalu PILIH, lalu KELOMPOK OLEH, ...) tetapi saya tidak memiliki referensi untuk itu
Joni
Beberapa contoh: beberapa klaim bahwa bagi mereka select @code:=sum(2), 2*@codebekerja di MySQL 5.5, tetapi bagi saya di 5.6 kolom kedua menghasilkan NULL pada doa pertama, dan mengembalikan 2 kali hasil sebelumnya ketika dijalankan lagi. Cukup menarik, baik pilih @code:=2, 2*@codedan select @code:=rand(), 2*@codememang berfungsi di 5.6 saya (hari ini). Tapi itu memang menulis dan membaca dalam klausa SELECT; dalam kasus Anda, Anda mengaturnya di WHERE.
Arjan
@Joni, Kenapa tidak mengevaluasi saja kondisinya dua kali? Tentunya MySQL cukup pintar untuk mengoptimalkan itu .......
Pacerier
@Pacerier harus mengulangi ekspresi masih lebih buruk terutama jika itu rumit. Saya belum dapat mengkonfirmasi apakah MySQL mengimplementasikan penghapusan subekspresi umum.
Joni
16

Mungkin jawaban saya terlambat tetapi ini bisa membantu orang lain.

Anda dapat melampirkannya dengan pernyataan pilih lain dan menggunakan mana klausa untuk itu.

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

calcAlias ​​adalah kolom alias yang dihitung.

George Khouri
sumber
Bagus dan pendek, tetapi ini terlalu samar untuk berguna.
Agamemnus
@ Agamemnus, apa maksudmu dengan itu?
Pacerier
Pertanyaannya adalah, "mengapa saya tidak dapat menggunakan kolom palsu di mana klausa dari permintaan DB yang sama?" Jawaban ini tidak menjawab pertanyaan itu dan tidak memiliki kata kerja.
Agamemnus
Maka cukup gunakan HAVING
Hett
8

Anda dapat menggunakan klausa HAVING untuk filter yang dihitung dalam bidang SELECT dan alias

Hett
sumber
@ fahimg23 - Tidak yakin. Saya mencoba mencari alasan mengapa, tetapi saya tidak bisa! Ingatlah perbedaan antara WHEREdan HAVING. Mereka tidak identik. stackoverflow.com/search?q=where+vs+having
rinogo
UPDATE: Itu karena jawaban ini memberikan solusi yang sama tetapi dengan lebih banyak info.
rinogo
1

Saya menggunakan mysql 5.5.24 dan kode berikut berfungsi:

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

SQL standar melarang referensi ke alias kolom dalam klausa WHERE. Pembatasan ini diberlakukan karena ketika klausa WHERE dievaluasi, nilai kolom mungkin belum ditentukan. Misalnya, kueri berikut ini ilegal:

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

Pavan Rajput
sumber
0

Anda dapat menggunakan SUBSTRING ( locations. raw, -6,4) untuk kondisi mana

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
sumber