DHDataHọc
Lý thuyếtBài 7/306 phút đọc+10 XP

Mệnh đề WHERE

Bạn sẽ học được

  • Hiểu WHERE lọ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 SELECT chưa dùng được trong WHERE.
  • 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ướcMệnh đềViệc được làm
1FROMXác định bảng nguồn
2WHERELoại các dòng không thỏa điều kiện
3SELECTChọn cột, tính biểu thức, đặt bí danh
4ORDER BYSắp xếp các dòng còn lại
5LIMITCắ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:

code
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):

code
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ộtLiteral đúngLiteral 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:

code
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:

idcustomer_idorder_datestatus
112025-01-05completed
222025-01-18completed
332025-02-02completed
412025-02-20completed
622025-03-15completed

Thử lọc theo trạng thái khác bằng cách đổi đúng một giá trị:

code
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:

code
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

  1. 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ị status còn thiếu bằng SELECT DISTINCT status FROM orders;.
  2. Viết truy vấn lấy các đơn từ ngày 2025-06-01 trở đi, không lọc trạng thái. Bạn sẽ thấy 7 dòng; trong đó có đơn pending và cancelled — bằng chứng rằng WHERE chỉ áp dụng đúng điều kiện bạn viết.
  3. 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.
  4. 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ứngNguyên nhânCách sửa
column "gia_sau_thue" does not existDùng bí danh SELECT trong WHERELặp biểu thức hoặc dùng truy vấn con
column "hanoi" does not existChuỗi thiếu dấu nháy đơnViế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ìnhDùng một dấu = để so sánh bằng
Truy vấn trả 0 dòng dù bảng có dữ liệuSai giá trị, sai chính tả, hoặc khác hoa/thườngChạy SELECT DISTINCT cot để xem giá trị thật
syntax error at or near "WHERE"Đặt WHERE sau ORDER BYWHERE 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

  • WHERE giữ 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í danh SELECT chưa dùng được trong WHERE.
  • 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