Sửa lỗi PostgreSQL ERROR: aggregate functions are not allowed in WHERE

beginner🐘 PostgreSQL2026-05-28| PostgreSQL (Tất cả phiên bản), Linux/macOS/Windows, SQL Client như psql, pgAdmin, hoặc DBeaver.

Error Message

ERROR: aggregate functions are not allowed in WHERE
#postgresql#sql#aggregate#where#having#count#sum

Vấn đềBạn đang cố gắng lọc kết quả truy vấn dựa trên một phép tính—như SUM(), COUNT(), hoặc AVG()—nhưng PostgreSQL không chấp nhận điều này. Có lẽ bạn đã viết một câu truy vấn trông như thế này:

-- Truy vấn này sẽ gây ra lỗi
SELECT user_id, COUNT(order_id)
FROM orders
WHERE COUNT(order_id) > 10
GROUP BY user_id;

Ngay khi bạn nhấn thực thi, bạn sẽ gặp phải trở ngại này:

ERROR: aggregate functions are not allowed in WHERE

Tại sao lỗi này xảy raHãy tưởng tượng một câu truy vấn SQL như một dây chuyền lắp ráp trong nhà máy. PostgreSQL không xử lý lệnh của bạn từ trên xuống dưới. Thay vào đó, nó tuân theo một trình tự logic nghiêm ngặt để đảm bảo tính toàn vẹn của dữ liệu. Dưới đây là thứ tự hoạt động:

  • FROM / JOIN: Cơ sở dữ liệu thu thập nguyên liệu thô (các bảng của bạn).- WHERE: Loại bỏ các hàng đơn lẻ không khớp với tiêu chí trước khi bất kỳ quá trình gom nhóm nào diễn ra.- GROUP BY: Sắp xếp các hàng còn lại vào các nhóm hoặc ngăn chứa.- Aggregation: Thực hiện các phép toán trên các nhóm đó (tính SUM hoặc COUNT).- HAVING: Lọc các nhóm dựa trên kết quả tính toán.- SELECT: Cuối cùng, quyết định những cột nào sẽ được hiển thị cho bạn.Lỗi xảy ra vì bạn đang cố gắng sử dụng một phép tính ở cấp độ nhóm (hàm gộp) trong bước WHERE. Tại thời điểm đó, cơ sở dữ liệu thậm chí còn chưa tạo ra các nhóm. Nó không thể lọc dựa trên một con số chưa tồn tại.

Giải pháp 1: Mệnh đề HAVINGMệnh đề HAVING là công cụ đắc lực cho tình huống này. Nó hoạt động chính xác như mệnh đề WHERE, nhưng nó đợi cho đến khi việc gom nhóm hoàn tất mới thực hiện nhiệm vụ của mình.

Cách khắc phục:- Loại bỏ hàm gộp khỏi mệnh đề WHERE.- Chèn mệnh đề HAVING ngay sau câu lệnh GROUP BY.```

-- Cách làm đúng SELECT user_id, COUNT(order_id) as total_orders FROM orders GROUP BY user_id HAVING COUNT(order_id) > 10;


## Giải pháp 2: Sử dụng Common Table Expression (CTE)Đối với các báo cáo phức tạp—chẳng hạn như tìm khách hàng đã chi hơn 5.000$ qua 20 giao dịch khác nhau—một CTE (mệnh đề `WITH`) giúp mã nguồn của bạn dễ đọc hơn. Về cơ bản, nó tạo ra một bảng tạm thời mà bạn có thể truy vấn như bình thường.

-- Sử dụng CTE để rõ ràng hơn WITH customer_stats AS ( SELECT user_id, SUM(amount) as total_spent FROM payments GROUP BY user_id ) SELECT * FROM customer_stats WHERE total_spent > 5000;


Trong phiên bản này, phép gộp được đặt bên trong khối `customer_stats`. Đến khi lệnh `SELECT` cuối cùng chạy, `total_spent` được coi như một cột dữ liệu tiêu chuẩn, cho phép bạn sử dụng bộ lọc `WHERE` quen thuộc.
## Cách kiểm tra kết quảViệc kiểm tra lại logic của bạn rất dễ dàng. Nếu truy vấn của bạn cho biết một người dùng có 12 đơn hàng, hãy chạy một bước kiểm tra nhanh cho ID cụ thể đó:

SELECT COUNT(*) FROM orders WHERE user_id = 742; -- Thay thế bằng một ID từ kết quả của bạn


Nếu số lượng đếm thủ công khớp với kết quả gộp, logic truy vấn của bạn đã chính xác.
## Mẹo tối ưu hiệu năng- **WHERE dành cho hàng:** Sử dụng nó để loại bỏ dữ liệu không liên quan sớm (ví dụ: `WHERE status = 'active'`). Điều này giúp truy vấn của bạn nhanh hơn.- **HAVING dành cho nhóm:** Chỉ sử dụng nó cho các phép tính yêu cầu xem xét nhiều hàng cùng một lúc.- **Kết hợp cả hai:** Đừng đưa mọi thứ vào `HAVING`. Loại bỏ 1.000.000 hàng 'đã hủy' bằng `WHERE` sẽ nhanh hơn nhiều so với việc gom nhóm tất cả chúng rồi mới lọc sau.Ví dụ về một truy vấn hiệu năng cao:

SELECT category, AVG(price) FROM products WHERE in_stock = true -- Lọc cấp độ hàng (Nhanh) GROUP BY category HAVING AVG(price) > 150; -- Lọc cấp độ nhóm

Related Error Notes