Cơ sở dữ liệu 03/09/2026 10 phút

WHERE diem <> 7 nuốt mất 200.000 hàng: cái bẫy NULL mà mọi lập trình viên SQL đều vấp

Viết WHERE diem <> 7 để lấy mọi hàng không phải 7, đợi 990000, đo ra 790000 — 200000 hàng NULL biến mất lặng lẽ. NULL không phải giá trị, so sánh với nó ra 'unknown' và WHERE loại sạch. NOT IN với một NULL trả 0 hàng, AVG bỏ NULL, và hàng toàn NULL còn nhỏ hơn trên đĩa. Logic ba trạng thái của SQL, đo tận nơi.

Cơ sở dữ liệu 03/09/2026 9 phút

Merge join: 77ms khi dữ liệu đã sắp, 160ms khi phải tự sắp — và vì sao đừng vội chê nó thua hash

Merge join trên khóa đã sắp: 77ms không cần sort; nhưng trên cột chưa sắp phải thêm Sort tràn đĩa, 160ms (hash chỉ 78ms). Tôi suýt kết luận 'merge dở hơn hash' — cho tới khi thấy mình đo nó ở đúng điều kiện bất lợi. Đổi lại nó giữ nguyên thứ tự: ORDER BY LIMIT nhanh 0,03ms so với 72ms của hash.

Cơ sở dữ liệu 03/09/2026 9 phút

Có index trên email mà WHERE lower(email) vẫn quét cả triệu hàng — và cú chữa hụt

Có index trên cột email nhưng WHERE lower(email)='...' vẫn quét cả bảng 7352 trang — một hàm bọc cột làm index cột trần vô dụng. Tạo index thẳng trên lower(email) thì cùng truy vấn còn 4 trang, nhanh gần 1800 lần. Nhưng đổi sang upper(email) là seq scan lại ngay, vì planner khớp biểu thức theo cú pháp chứ không theo ý nghĩa.

Cơ sở dữ liệu 03/09/2026 8 phút

ORDER BY ... LIMIT 10 không sắp cả triệu hàng như bạn tưởng — nó chỉ giữ 10, tốn 25kB

ORDER BY trên 1 triệu hàng: work_mem nhỏ thì external merge tràn đĩa 208ms, lớn thì quicksort RAM 162ms. Nhưng ORDER BY LIMIT 10 dùng top-N heapsort chỉ 25kB, 53ms — không sắp hết. Đừng đoán thuật toán từ câu SQL; EXPLAIN cho biết cơ sở dữ liệu thật sự sắp thế nào, và một index có thể bỏ luôn bước sắp.

Cơ sở dữ liệu 03/09/2026 8 phút

5 tiến trình cùng quét bảng, nhưng chỉ nhanh 3,2 lần — vì sao song song không nhân đủ

Tưởng 2 worker (3 tiến trình) thì quét nhanh 3 lần, đo ra chỉ 2,6 lần; 4 worker chỉ 3,2 lần. Song song không nhân tuyến tính vì bước Gather, phần tuần tự và cái đĩa dùng chung (định luật Amdahl). Và xin 6 worker chỉ được 4 — planner cấp theo cỡ bảng; bảng nhỏ thì PostgreSQL chẳng thèm song song. Khi nào một index thắng xa việc thêm worker.

Cơ sở dữ liệu 03/09/2026 8 phút

0,001ms trong EXPLAIN — nhưng nó chạy 1999 lần: cái bẫy loops của nested loop join

Nested loop join với bảng ngoài nhỏ + bảng trong có index chỉ 0,027ms; cùng 2000 hàng nhưng bỏ index thì 156ms — chậm 83 lần. Và tôi suýt tưởng join miễn phí vì đọc actual time 0,001ms mà quên nhân với loops=1999. Đọc EXPLAIN phải nhân thời gian node trong với số vòng lặp.