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?

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.


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.


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.


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.


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


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

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;


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'
)
)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.