Mệnh đề WHERE
Bạn sẽ học được
- Hiểu
WHERElọc dòng trước khi dữ liệu được chọn, sắp xếp hay giới hạn. - Viết điều kiện lọc trên số, chuỗi và ngày bằng đúng kiểu literal.
- Đọc thứ tự thực thi logic để biết vì sao bí danh
SELECTchưa dùng được trongWHERE. - Kiểm tra kết quả bằng cách đếm dòng thay vì nhìn lướt.
- Nhận ra lỗi phổ biến nhất: lọc đúng cú pháp nhưng sai bảng hoặc sai giá trị.
Khái niệm cốt lõi
WHERE là mệnh đề trả lời câu hỏi: dòng nào được giữ lại? Không có WHERE, truy vấn đọc toàn bộ bảng. Có WHERE, mỗi dòng được đem ra kiểm tra một điều kiện; dòng nào cho kết quả đúng thì đi tiếp, dòng sai bị loại.
Một cách hình dung: bạn chụp ảnh một lớp học. SELECT quyết định chụp những gì trong khung hình (cột), WHERE quyết định ai được có mặt trong ảnh (dòng). Hai việc độc lập. Chọn ít cột không làm ít người hơn, và lọc bớt người không làm đổi danh sách cột.
Thứ tự logic của một câu SELECT:
| Bước | Mệnh đề | Việc được làm |
|---|---|---|
| 1 | FROM | Xác định bảng nguồn |
| 2 | WHERE | Loại các dòng không thỏa điều kiện |
| 3 | SELECT | Chọn cột, tính biểu thức, đặt bí danh |
| 4 | ORDER BY | Sắp xếp các dòng còn lại |
| 5 | LIMIT | Cắt bớt số dòng cuối cùng |
Thứ tự này giải thích một lỗi rất hay gặp:
SELECT price * 1.1 AS gia_sau_thue
FROM products
WHERE gia_sau_thue > 1000000;Truy vấn báo column "gia_sau_thue" does not exist, dù bí danh nằm ngay phía trên. Nguyên nhân: WHERE chạy trước SELECT, nên lúc lọc bí danh chưa tồn tại. Cách sửa là lặp biểu thức, hoặc bọc truy vấn con (sẽ học ở khóa nâng cao):
SELECT price * 1.1 AS gia_sau_thue
FROM products
WHERE price * 1.1 > 1000000;Điều kiện trong WHERE là một biểu thức luận lý. Nó trả về true, false hoặc unknown (khi có NULL). Chỉ dòng cho true được giữ. Đây là lý do một điều kiện viết đúng cú pháp vẫn có thể trả về 0 dòng: nó không sai, chỉ là không dòng nào thỏa.
Kiểu dữ liệu của literal phải khớp với cột:
| Cột | Literal đúng | Literal sai |
|---|---|---|
Số (price) | 500000 | '500000' (so sánh chuỗi) |
Chuỗi (city) | 'Hà Nội' | Hà Nội (hiểu là tên cột) |
Ngày (order_date) | DATE '2025-01-01' | '01/01/2025' (phụ thuộc định dạng) |
PostgreSQL thường tự chuyển đổi kiểu, nên price > '500000' có thể vẫn chạy. Nhưng dựa vào chuyển đổi ngầm là thói quen xấu: nó làm chỉ mục kém hiệu quả và khiến lỗi khó thấy hơn khi dữ liệu đổi kiểu.
Ví dụ chạy được
Bảng orders có 16 đơn, mỗi đơn có status là completed, pending hoặc cancelled. Lấy các đơn đã hoàn tất và sắp theo ngày:
SELECT id, customer_id, order_date, status
FROM orders
WHERE status = 'completed'
ORDER BY order_date;Kết quả có 13 dòng — nhiều hơn bạn có thể đếm nhanh bằng mắt. Ba đơn còn lại gồm hai pending (id 5 và 14) và một cancelled (id 10). Năm dòng đầu:
| id | customer_id | order_date | status |
|---|---|---|---|
| 1 | 1 | 2025-01-05 | completed |
| 2 | 2 | 2025-01-18 | completed |
| 3 | 3 | 2025-02-02 | completed |
| 4 | 1 | 2025-02-20 | completed |
| 6 | 2 | 2025-03-15 | completed |
Thử lọc theo trạng thái khác bằng cách đổi đúng một giá trị:
SELECT id, order_date
FROM orders
WHERE status = 'pending';Hai dòng: đơn 5 ngày 2025-03-03 và đơn 14 ngày 2025-09-14. Đổi thành 'cancelled' còn đúng một dòng, đơn 10 ngày 2025-06-01.
Ba truy vấn trên cộng lại cho 13 + 2 + 1 = 16, bằng toàn bộ bảng. Phép cộng này không phải trò chơi: nó là cách kiểm tra điều kiện lọc có bỏ sót nhóm nào không. Nếu ba con số cộng lại nhỏ hơn 16, nghĩa là có giá trị status khác mà bạn chưa biết — một khám phá đáng giá.
Thêm một điều kiện số để thấy WHERE không chỉ so sánh bằng:
SELECT id, order_date
FROM orders
WHERE status = 'completed'
AND order_date >= DATE '2025-06-01'
ORDER BY order_date;Còn 4 dòng: đơn 12 (2025-07-04), 13 (2025-08-09), 15 (2025-10-01) và 16 (2025-11-11). Đơn 8 ngày 2025-04-12 đã hoàn tất nhưng bị loại vì trước tháng 6. Đọc kết quả theo cách này giúp bạn phân biệt "điều kiện sai" với "dữ liệu không có".
Một mẹo thực hành: trước khi thêm điều kiện, chạy SELECT COUNT(*) FROM orders; để biết tổng. Sau đó chạy SELECT COUNT(*) ... WHERE ... với từng điều kiện. So sánh các con số trước khi nhìn dữ liệu chi tiết. Rẻ hơn nhiều so với đọc 13 dòng và tự hỏi mình có bỏ sót không.
Thực hành
- Chạy truy vấn đếm đơn theo từng trạng thái và cộng lại. Kết quả phải bằng 16. Nếu không, tìm giá trị
statuscòn thiếu bằngSELECT DISTINCT status FROM orders;. - Viết truy vấn lấy các đơn từ ngày
2025-06-01trở đi, không lọc trạng thái. Bạn sẽ thấy 7 dòng; trong đó có đơnpendingvàcancelled— bằng chứng rằngWHEREchỉ áp dụng đúng điều kiện bạn viết. - Lấy tên khách ở Hà Nội, sắp theo tên:
SELECT name FROM customers WHERE city = 'Hà Nội' ORDER BY name;. Kết quả ba dòng: An Nguyễn, Dũng Phạm, Khánh Hồ. Bài luyện Lọc khách Hà Nội ở cuối chương dùng đúng truy vấn này. - Trước mỗi lần bấm chạy, tự dự đoán số dòng. Nếu dự đoán và kết quả lệch, đó là chỗ cần hiểu rõ hơn — không phải chỗ cần sửa mò.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
column "gia_sau_thue" does not exist | Dùng bí danh SELECT trong WHERE | Lặp biểu thức hoặc dùng truy vấn con |
column "hanoi" does not exist | Chuỗi thiếu dấu nháy đơn | Viết 'Hà Nội', không viết Hà Nội |
syntax error at or near "==" | Dùng == theo thói quen lập trình | Dùng một dấu = để so sánh bằng |
| Truy vấn trả 0 dòng dù bảng có dữ liệu | Sai giá trị, sai chính tả, hoặc khác hoa/thường | Chạy SELECT DISTINCT cot để xem giá trị thật |
syntax error at or near "WHERE" | Đặt WHERE sau ORDER BY | WHERE luôn đứng trước ORDER BY |
Lỗi cuối cùng đáng nhớ vì nó phản ánh thứ tự logic ở trên. SQL không đọc câu lệnh từ trái sang phải như tiếng Việt; nó thực thi theo thứ tự FROM → WHERE → SELECT → ORDER BY → LIMIT, dù bạn viết theo thứ tự nào. Viết đúng thứ tự quy ước giúp câu lệnh dễ đọc và tránh lỗi.
Một lỗi tinh vi hơn: WHERE city = 'hà nội' không khớp 'Hà Nội' vì PostgreSQL so sánh chuỗi có phân biệt hoa/thường. Nếu cần không phân biệt, dùng LOWER(city) = LOWER('Hà Nội') hoặc ILIKE — nội dung của bài lọc chuỗi.
Tóm tắt
WHEREgiữ các dòng có điều kiện đúng; nó không ảnh hưởng đến danh sách cột.- Thứ tự logic là
FROM → WHERE → SELECT → ORDER BY → LIMIT, nên bí danhSELECTchưa dùng được trongWHERE. - Literal phải đúng kiểu: số không nháy, chuỗi nháy đơn, ngày dùng
DATE 'YYYY-MM-DD'. - Kiểm tra bằng phép đếm: tổng các nhóm phải khớp tổng bảng.
- Dự đoán số dòng trước khi chạy là cách nhanh nhất để phát hiện điều kiện sai.
Dùng ← → để chuyển bài