Sử dụng bí danh cột trong mệnh đề WHERE của truy vấn MySQL tạo ra lỗi


201

Truy vấn tôi đang chạy như sau, tuy nhiên tôi đang gặp lỗi này:

# 1054 - Cột không xác định 'notify_postcode' trong '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'
 )
)

Câu hỏi của tôi là: tại sao tôi không thể sử dụng cột giả trong mệnh đề where của cùng một truy vấn DB?

Câu trả lời:


434

Bạn chỉ có thể sử dụng các bí danh cột trong các mệnh đề GROUP BY, ORDER BY hoặc HAVING.

SQL chuẩn không cho phép bạn tham khảo bí danh cột trong mệnh đề WHERE. Hạn chế này được áp đặt vì khi mã WHERE được thực thi, giá trị cột có thể chưa được xác định.

Sao chép từ tài liệu MySQL

Như đã chỉ ra trong các ý kiến, sử dụng HAVING thay vào đó có thể thực hiện công việc. Hãy chắc chắn để đọc ở đây WHERE vs HAVING .


1
Chúc mừng cho phản ứng nhanh chóng và chính xác! Tôi đã xem xét mệnh đề HAVING và tìm ra cách để chạy thành công truy vấn này. Cảm ơn một lần nữa.
James

38
Trong trường hợp bất kỳ ai khác có cùng một vấn đề như tôi đã sử dụng col bí danh trong một mệnh đề không thành công - hoán đổi 'WHERE' cho 'HAVING đã sửa nó ngay lập tức +1 câu trả lời tốt.
megaSteve4

@ megaSteve4 Tôi cũng gặp vấn đề tương tự! Sử dụng "HAVING" đã giải quyết nó một cách trơn tru. :)
Johan

9
Điều này có thể hoặc không quan trọng trong trường hợp của bạn, nhưng HAVINGthực thi chậm hơnWHERE
DTs

1
Lý do havinghoạt động là vì các giá trị cột phải được tính theo thời gian bạn đến having. Đây không phải là trường hợp với where, như đã nêu ở trên.
Millie Smith

24

Như Victor đã chỉ ra, vấn đề là với bí danh. Tuy nhiên, điều này có thể tránh được bằng cách đặt biểu thức trực tiếp vào mệnh đề 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'
 )
)

Tuy nhiên, tôi đoán điều này rất không hiệu quả, vì truy vấn con phải được thực thi cho mỗi hàng của truy vấn bên ngoài.


1
@rodion, Có, tôi tin rằng điều này rất chậm và không hiệu quả.
Pacerier

20

SQL chuẩn (hoặc MySQL) không cho phép sử dụng bí danh cột trong mệnh đề WHERE vì

khi mệnh đề WHERE được ước tính, giá trị cột có thể chưa được xác định.

(từ tài liệu MySQL ). Những gì bạn có thể làm là tính giá trị cột trong mệnh đề WHERE , lưu giá trị trong một biến và sử dụng nó trong danh sách trường. Ví dụ bạn có thể làm điều này:

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

Điều này tránh lặp lại biểu thức khi nó phát triển phức tạp, làm cho mã dễ bảo trì hơn.


9
Điều này không mâu thuẫn với tài liệu có nội dung "Theo quy tắc chung, bạn không bao giờ nên gán giá trị cho biến người dùng và đọc giá trị trong cùng một tuyên bố. Bạn có thể nhận được kết quả mà bạn mong đợi, nhưng điều này không được bảo đảm." ?
Arjan

Đó chắc chắn là điều cần lưu ý. Nó luôn luôn làm việc cho tôi, tôi nghĩ rằng thứ tự đánh giá các phần khác nhau của một tuyên bố phải được sửa (đầu tiên là WHERE, sau đó CHỌN, sau đó NHÓM THEO, ...) nhưng tôi không có tài liệu tham khảo cho điều đó
Joni

Một vài ví dụ: một số tuyên bố rằng đối với chúng select @code:=sum(2), 2*@codehoạt động trong MySQL 5.5, nhưng đối với tôi trong 5.6, cột thứ hai mang lại NULL cho lần gọi đầu tiên và trả về 2 lần kết quả trước đó khi chạy lại. Thật thú vị, cả hai lựa chọn @code:=2, 2*@codeselect @code:=rand(), 2*@codedường như làm việc trong 5.6 của tôi (ngày hôm nay). Nhưng đó thực sự là viết và đọc trong mệnh đề SELECT; trong trường hợp của bạn, bạn đang đặt nó ở ĐÂU.
Arjan

@Joni, Tại sao không chỉ đánh giá điều kiện hai lần? Chắc chắn MySQL đủ thông minh để tối ưu hóa điều đó .......
Pacerier

@Pacerier phải lặp lại biểu thức vẫn tệ hơn đặc biệt là nếu nó phức tạp. Tôi đã không thể xác nhận nếu MySQL thực hiện loại bỏ phổ biến phụ.
Joni

16

Có thể câu trả lời của tôi là quá muộn nhưng điều này có thể giúp đỡ người khác.

Bạn có thể kèm theo nó với một câu lệnh chọn khác và sử dụng mệnh đề where cho nó.

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

calcAlias ​​là cột bí danh đã được tính toán.


Đẹp và ngắn, nhưng điều này quá mơ hồ để có ích.
Agamemnus

@Agamemnus, ý của bạn là gì?
Pacerier

Câu hỏi là "tại sao tôi không thể sử dụng cột giả trong mệnh đề where của cùng một truy vấn DB?" Câu trả lời này không trả lời câu hỏi đó và thiếu một động từ.
Agamemnus

Sau đó, chỉ cần sử dụng HAVING
Hett

8

Bạn có thể sử dụng mệnh đề HAVING cho bộ lọc được tính trong các trường và bí danh CHỌN


@ fahimg23 - Không chắc chắn. Tôi đã cố gắng tìm một lý do tại sao, nhưng tôi không thể! Hãy ghi nhớ sự khác biệt giữa WHEREHAVING, mặc dù. Chúng không giống nhau. stackoverflow.com/search?q=where+vs+having
rinogo

CẬP NHẬT: Đó là bởi vì câu trả lời này cung cấp cùng một giải pháp nhưng có thêm thông tin.
rinogo

1

Tôi đang sử dụng mysql 5.5.24 và đoạn mã sau hoạt động:

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 chuẩn không cho phép các tham chiếu đến các bí danh cột trong mệnh đề WHERE. Hạn chế này được áp đặt vì khi mệnh đề WHERE được ước tính, giá trị cột có thể chưa được xác định. Ví dụ: truy vấn sau đây là bất hợp pháp:

CHỌN id, COUNT (*) NHƯ cnt Tbl_name WHERE cnt> 0 GROUP BY id;


0

Bạn có thể sử dụng SUBSTRING ( locations. raw, -6,4) cho nơi 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'
)
)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.