AND, OR và NULL
Bạn sẽ học được
- Kết hợp nhiều điều kiện bằng
AND,OR,NOTvà kiểm soát thứ tự bằng ngoặc. - Hiểu vì sao
ANDđược ưu tiên hơnORvà cách tránh nhầm lẫn. - Phân biệt
NULLvới chuỗi rỗng và số 0. - Dùng
IS NULL,IS NOT NULL,COALESCEđể xử lý dữ liệu thiếu. - Nhận ra bẫy
NOT INkhi danh sách cóNULL.
Khái niệm cốt lõi
Một điều kiện thường chưa đủ. "Khách ở Hà Nội và thuộc nhóm retail", "đơn đã hoàn tất hoặc đang chờ" — đó là lúc dùng AND và OR.
AND chỉ đúng khi cả hai vế đúng. OR đúng khi ít nhất một vế đúng. NOT đảo giá trị. Ba toán tử này tạo thành biểu thức luận lý, và cũng như phép nhân trước phép cộng trong số học, chúng có thứ tự ưu tiên: NOT trước, rồi AND, rồi OR.
Điều này khiến câu sau đây nguy hiểm:
SELECT name, city, segment
FROM customers
WHERE city = 'Hà Nội' AND segment = 'retail' OR city = 'Đà Nẵng';Vì AND được tính trước, câu này tương đương:
WHERE (city = 'Hà Nội' AND segment = 'retail') OR city = 'Đà Nẵng'Kết quả là An Nguyễn (Hà Nội, retail), Chi Lê và Linh Ngô (Đà Nẵng) — ba dòng. Nếu ý định là "khách Hà Nội hoặc Đà Nẵng, và đều thuộc retail", cần viết:
WHERE (city = 'Hà Nội' OR city = 'Đà Nẵng') AND segment = 'retail'Kết quả vẫn là An, Chi, Linh — ba dòng trong trường hợp này, nhưng vì lý do khác: Dũng và Khánh ở Hà Nội nhưng thuộc wholesale, nên bị loại. Sự trùng hợp kết quả không chứng minh hai câu tương đương. Hãy thử đổi segment thành wholesale để thấy chúng tách nhau ngay.
Quy tắc thực hành: luôn đặt ngoặc khi trộn AND với OR, kể cả khi bạn chắc mình hiểu thứ tự ưu tiên. Ngoặc không làm truy vấn chậm; chúng làm ý định rõ ràng cho người đọc và cho chính bạn sáu tháng sau.
NULL là giá trị đặc biệt, nghĩa là "không có thông tin". Nó không phải chuỗi rỗng '', không phải số 0, và không bằng chính nó. Ba điều cần nhớ:
| Biểu thức | Kết quả |
|---|---|
NULL = NULL | unknown (không phải true) |
NULL <> 5 | unknown |
NULL AND true | unknown |
NULL OR true | true |
NULL AND false | false |
WHERE chỉ giữ các dòng cho true. Một dòng có email là NULL sẽ bị loại bởi cả email = 'an@example.com' lẫn email <> 'an@example.com'. Đây là lý do điều kiện "khác" không đếm được dữ liệu thiếu.
Muốn kiểm tra NULL, dùng toán tử chuyên dụng:
SELECT name FROM customers WHERE email IS NULL; -- 2 dòng
SELECT name FROM customers WHERE email IS NOT NULL; -- 10 dòngHai kết quả cộng lại bằng 12, đúng toàn bộ bảng. Phép kiểm tra này quan trọng: nếu bạn đếm khách "có email" bằng email <> '', bạn sẽ nhận 10 dòng nhưng vì lý do khác, và con số đó sẽ sai ngay khi có người nhập chuỗi rỗng.
Khi cần hiển thị giá trị thay thế, dùng COALESCE:
SELECT name, COALESCE(email, '(chưa cập nhật)') AS email
FROM customers
ORDER BY id;COALESCE trả về đối số đầu tiên không NULL. Nó hữu ích cho báo cáo, nhưng đừng dùng nó để che dữ liệu thiếu trong câu WHERE — lọc và hiển thị là hai việc khác nhau.
Có một bẫy nâng cao với NOT IN: nếu danh sách chứa NULL, toàn bộ phép so sánh trở thành unknown, và truy vấn trả 0 dòng dù dữ liệu có. Ví dụ email NOT IN ('a@example.com', NULL) không trả về dòng nào. Cách an toàn là lọc NULL trước hoặc dùng NOT EXISTS. Với dữ liệu của khóa học, city không bao giờ NULL, nên city NOT IN ('Hà Nội', 'Đà Nẵng') chạy đúng — nhưng hãy nhớ quy tắc khi gặp cột cho phép NULL.
Ví dụ chạy được
Khách Hà Nội thuộc nhóm retail:
SELECT name, city, segment
FROM customers
WHERE city = 'Hà Nội' AND segment = 'retail';Một dòng: An Nguyễn. Dũng Phạm và Khánh Hồ cũng ở Hà Nội nhưng thuộc wholesale, nên bị AND loại.
Khách ở Hà Nội hoặc Đà Nẵng:
SELECT name, city
FROM customers
WHERE city = 'Hà Nội' OR city = 'Đà Nẵng'
ORDER BY city, name;Năm dòng: ba ở Hà Nội (An Nguyễn, Dũng Phạm, Khánh Hồ) và hai ở Đà Nẵng (Chi Lê, Linh Ngô).
Kết hợp phủ định:
SELECT name, city
FROM customers
WHERE NOT city = 'Hà Nội'
AND NOT city = 'Đà Nẵng'
ORDER BY city;Bảy dòng còn lại. Viết bằng city NOT IN ('Hà Nội', 'Đà Nẵng') gọn hơn và cho cùng kết quả, vì city là cột NOT NULL.
Tìm khách chưa có email ở Hà Nội:
SELECT name, email, city
FROM customers
WHERE email IS NULL AND city = 'Hà Nội';Một dòng: Dũng Phạm. Inh Đỗ cũng thiếu email nhưng ở TP. Hồ Chí Minh.
Đếm để thấy ảnh hưởng của NULL:
SELECT COUNT(*) AS co_email FROM customers WHERE email IS NOT NULL; -- 10
SELECT COUNT(*) AS khac_an FROM customers WHERE email <> 'an@example.com'; -- 9Dòng thứ hai trả 9, không phải 11. Trong 12 khách, hai người không có email nên không thể "khác an@example.com" — câu trả lời là "không biết", và WHERE loại luôn. Nếu muốn đếm cả người chưa có email, viết email IS NULL OR email <> 'an@example.com' và nhận 11.
Một truy vấn tổng hợp dùng cả ba toán tử:
SELECT name, city, segment
FROM customers
WHERE (city = 'Hà Nội' OR city = 'Đà Nẵng')
AND segment = 'retail'
ORDER BY name;Ba dòng: An Nguyễn, Chi Lê, Linh Ngô. Không có Dũng hay Khánh vì wholesale. Đây là dạng câu hỏi rất thường gặp trong phân tích: "trong nhóm khách này, những ai thuộc phân khúc kia".
Thực hành
- Viết truy vấn lấy khách thuộc nhóm
vipở TP. Hồ Chí Minh hoặc Cần Thơ. Dự đoán trước danh sách, rồi chạy. Sau đó bỏ ngoặc và giải thích kết quả thay đổi thế nào. - Đếm số khách có email và số khách không có email. Hai số phải cộng lại bằng 12. Thử thay
IS NULLbằng= NULLvà giải thích vì sao kết quả là 0. - Lấy các đơn
completedcóorder_datetừ tháng 6 trở đi. Kết quả bốn dòng. Viết lại bằngNOT (status <> 'completed' OR order_date < DATE '2025-06-01')và kiểm tra tính tương đương bằng De Morgan. - Dùng
COALESCEđể hiển thị'(chưa có)'cho email thiếu, rồi lọc riêng hai dòng đó. So sánh với việc lọc bằngIS NULLtrước khi hiển thị.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
Kết quả nhiều hơn dự kiến khi trộn AND/OR | AND được ưu tiên hơn OR | Đặt ngoặc quanh từng nhóm điều kiện |
email <> '...' bỏ sót khách | NULL làm điều kiện thành unknown | Thêm OR email IS NULL nếu muốn tính cả |
= NULL không trả dòng nào | NULL không bằng chính nó | Dùng IS NULL |
NOT IN trả 0 dòng dù có dữ liệu | Danh sách chứa NULL | Loại NULL khỏi danh sách hoặc dùng NOT EXISTS |
COALESCE che mất dữ liệu thiếu | Dùng thay thế ngay trong WHERE | Lọc bằng IS NULL, chỉ dùng COALESCE khi hiển thị |
Lỗi thứ nhất là lỗi tốn thời gian nhất vì kết quả vẫn "có vẻ đúng". Hãy tạo thói quen: mỗi khi câu truy vấn có cả AND và OR, đọc lại và tự hỏi "ngoặc ở đâu?". Nếu không trả lời được ngay, thêm ngoặc.
Tóm tắt
ANDưu tiên hơnOR; luôn dùng ngoặc khi trộn hai toán tử.NOTđảo giá trị; có thể thayNOT INbằng chuỗi điều kiện<>khi cần kiểm soátNULL.NULLlà "không biết", không bằng chính nó; dùngIS NULL/IS NOT NULL.- Điều kiện "khác" không đếm được dòng có
NULL; cộng thêmIS NULLnếu cần. COALESCEdùng khi hiển thị, không dùng để lách điều kiện lọc.
Dùng ← → để chuyển bài