Bài 3. Lọc nâng cao: LIKE, IN, BETWEEN, NULL, CASE

Bài này mở rộng WHERE với các toán tử thường dùng khi làm tìm kiếm và báo cáo: LIKE với ký tự đại diện, IN, BETWEEN, xử lý NULL đúng cách, và biểu thức CASE để phân loại ngay trong truy vấn.

40 phútCơ bản
Bạn sẽ học được
  • Tìm kiếm theo mẫu với LIKE, % và _
  • Dùng IN, NOT IN, BETWEEN
  • Hiểu logic ba giá trị và IS NULL, IFNULL, COALESCE
  • Phân loại bằng CASE WHEN

Khái niệm và lý thuyết

Mọi so sánh với NULL cho kết quả UNKNOWN, không phải TRUE: WHERE email = NULL không trả về dòng nào. Phải dùng IS NULL/IS NOT NULL. NULL nghĩa là “chưa biết”, khác chuỗi rỗng và khác 0; hàm tổng hợp bỏ qua NULL.

LIKE và ký tự đại diện

SELECT ho_ten FROM sinhvien WHERE ho_ten LIKE 'Lò%';        -- bắt đầu bằng Lò
SELECT ho_ten FROM sinhvien WHERE ho_ten LIKE '%Thị%';      -- chứa Thị
SELECT ho_ten FROM sinhvien WHERE ho_ten LIKE '%An';        -- kết thúc bằng An
SELECT ma_sv FROM sinhvien WHERE ma_sv LIKE 'SV00_';        -- _ : đúng một ký tự
SELECT ho_ten FROM sinhvien WHERE ho_ten NOT LIKE '%Văn%';
SELECT ho_ten FROM sinhvien WHERE ho_ten REGEXP '^(Lò|Vì) ';  -- biểu thức chính quy

Tìm kiếm trong PHP: WHERE ho_ten LIKE ? với tham số "%$q%". LIKE bắt đầu bằng % không dùng được chỉ mục, chậm với bảng rất lớn; khi đó dùng FULLTEXT.

IN và BETWEEN

SELECT ho_ten, que_quan FROM sinhvien WHERE que_quan IN ('Sơn La', 'Điện Biên', 'Lai Châu');
SELECT ho_ten FROM sinhvien WHERE lop_id NOT IN (1, 2);
SELECT * FROM diem WHERE diem BETWEEN 7 AND 8.5;             -- bao gồm hai đầu
SELECT ho_ten FROM sinhvien WHERE ngay_sinh BETWEEN '2006-01-01' AND '2006-06-30';
SELECT ho_ten FROM sinhvien WHERE ngay_sinh NOT BETWEEN '2006-01-01' AND '2006-12-31';
-- IN với truy vấn con (bài JOIN nói thêm)
SELECT ho_ten FROM sinhvien WHERE id IN (SELECT sinhvien_id FROM diem WHERE diem >= 9);

NULL

SELECT ho_ten FROM sinhvien WHERE email IS NULL;             -- chưa có email
SELECT ho_ten FROM sinhvien WHERE email IS NOT NULL;
SELECT ho_ten, IFNULL(email, 'chưa có') AS email FROM sinhvien;
SELECT ho_ten, COALESCE(email, que_quan, 'N/A') FROM sinhvien;  -- giá trị đầu tiên khác NULL
SELECT * FROM diem WHERE diem IS NULL;                        -- chưa có điểm
SELECT AVG(diem) FROM diem;                                   -- NULL bị bỏ qua khi tính

CASE: phân loại trong truy vấn

SELECT sv.ho_ten, d.diem,
  CASE
    WHEN d.diem IS NULL THEN 'Chưa có điểm'
    WHEN d.diem >= 8.5 THEN 'Giỏi'
    WHEN d.diem >= 7   THEN 'Khá'
    WHEN d.diem >= 5   THEN 'Trung bình'
    ELSE 'Không đạt'
  END AS xep_loai
FROM diem d JOIN sinhvien sv ON sv.id = d.sinhvien_id;
-- Dạng ngắn khi so sánh bằng
SELECT ho_ten, CASE gioi_tinh WHEN 'Nam' THEN 'Anh' ELSE 'Chị' END AS xung_ho FROM sinhvien;
-- Sắp xếp theo thứ tự tùy ý
SELECT * FROM lop ORDER BY CASE khoa WHEN 'KHTN&CN' THEN 1 ELSE 2 END, ten_lop;
Mẹo hay

Khi so sánh ngày, viết theo định dạng YYYY-MM-DD trong nháy đơn; MySQL tự hiểu là kiểu DATE.

Lưu ý

NOT IN với danh sách có NULL (ví dụ từ truy vấn con) trả về rỗng: lọc IS NOT NULL trong truy vấn con hoặc dùng NOT EXISTS.

Thực hành
  1. Tìm sinh viên có họ Cầm hoặc Lường.
  2. Liệt kê sinh viên sinh trong quý 1 năm 2006.
  3. Liệt kê sinh viên chưa có email, hiển thị “chưa cập nhật” thay cho NULL.
  4. Xếp loại tất cả điểm bằng CASE và đếm số lượng mỗi loại (kết hợp GROUP BY ở bài 5).
Tóm tắt bài học
  • LIKE với % và _; REGEXP cho mẫu phức tạp.
  • IN, BETWEEN gọn hơn nhiều OR/AND.
  • NULL: dùng IS NULL, IFNULL, COALESCE.
  • CASE WHEN phân loại và sắp xếp tùy ý.