Menu

Toán tử và NULL trong SQLite: IS NULL, COALESCE, IFNULL

Cách các toán tử SQLite xử lý NULL: vì sao = và <> không hoạt động như bạn nghĩ, và các công cụ IS, IS NOT, COALESCE, IFNULL làm được điều đó.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

NULL nghĩa là "không xác định"

Mọi giá trị khác trong SQLite đều biểu diễn một thứ cụ thể: một số, một chuỗi, một blob. NULL thì khác. Nó là chỗ giữ cho một giá trị bị thiếu hoặc chưa biết. Chỉ một ý đó thôi đã giải thích mọi hành vi kỳ lạ của NULL trong truy vấn.

Hãy tạo một bảng nhỏ để thử nghiệm:

Hai cột cho phép null. Boris không có email. Cleo không có tuổi. Dan không có cả hai. Phần còn lại của trang nói về cách truy vấn những hàng như vậy mà không bị đánh lừa.

= và <> không dùng được với NULL

Phản xạ đầu tiên của bạn là viết WHERE email = NULL. Trông có vẻ hợp lý. Nhưng nó không trả về gì:

Không có hàng nào, dù rõ ràng Boris và Dan có email null. Lý do: so sánh bất cứ thứ gì với NULL đều cho ra NULL, không phải đúng hay sai. Mệnh đề WHERE của SQLite chỉ giữ những hàng có điều kiện là đúng, mà NULL thì không phải đúng. Vì vậy hàng đó bị lọc mất.

Cái bẫy tương tự với <>:

Bạn sẽ nghĩ câu này trả về mọi người trừ Ada. Nhưng nó chỉ trả về Cleo. Boris và Dan, có email null, bị loại, vì NULL <> 'ada@example.com' cũng là NULL, không phải đúng.

Đây là cái bẫy phổ biến nhất trong SQL. Bất cứ khi nào một truy vấn "mất hàng" ngoài dự kiến, hãy nghi ngờ một cột null.

Dùng IS NULL và IS NOT NULL

Cách đúng để kiểm tra null là toán tử IS. Khác với =, nó hiểu null và trả về đúng hoặc sai, không bao giờ trả về null:

Truy vấn đầu trả về Boris và Dan. Truy vấn thứ hai trả về Ada và Cleo. IS NULL và IS NOT NULL là hai toán tử được tạo ra riêng để hỏi "giá trị này có bị thiếu không?". Hãy dùng chúng ở bất cứ chỗ nào bạn định viết = NULL hay <> NULL.

Nếu muốn "không phải Ada, tính cả những giá trị chưa biết", hãy kết hợp các điều kiện một cách tường minh:

Giờ Boris, Cleo và Dan đều xuất hiện.

NULL lan truyền qua phép tính và phép nối chuỗi

Quy tắc "không xác định" không chỉ áp dụng cho phép so sánh. Mọi phép toán chạm tới null đều cho ra null:

next_year và doubled là null với Cleo và Dan. labelled_age cũng null với họ: nối một chuỗi với NULL cho ra NULL, không phải 'Age: '. Nếu một cột có thể null và bạn cần một giá trị dùng được ở đầu ra, bạn phải xử lý nó. Đó là lúc hai hàm tiếp theo phát huy tác dụng.

IFNULL: giá trị dự phòng với hai đối số

IFNULL(a, b) trả về a, trừ khi a là null thì trả về b. Đây là cách đơn giản nhất để thay null bằng một giá trị mặc định:

Boris và Dan nhận (no email). Cleo và Dan nhận 0. Dữ liệu gốc không thay đổi: IFNULL chỉ viết lại đầu ra.

IFNULL luôn nhận đúng hai đối số. Nếu cần nhiều giá trị dự phòng hơn, hãy dùng COALESCE.

COALESCE: giá trị khác NULL đầu tiên thắng

COALESCE(a, b, c, ...) duyệt các đối số từ trái sang phải và trả về đối số đầu tiên khác null. Nó mở rộng IFNULL cho số lượng giá trị dự phòng bất kỳ:

Với Ada và Cleo, email được dùng. Với Boris và Dan, email là null, nên SQLite thử đối số thứ hai: một địa chỉ được tạo từ tên. Nếu cả nó cũng null, kết quả sẽ rơi xuống 'anonymous'.

COALESCE là lựa chọn dùng được ở mọi nơi: mọi cơ sở dữ liệu SQL lớn đều hỗ trợ nó theo cùng một cách. IFNULL là tiện ích của SQLite/MySQL cho trường hợp hai đối số. Hãy mặc định chọn COALESCE; chỉ dùng IFNULL khi bạn thật sự chỉ có hai đối số và muốn tên ngắn hơn.

NULL không phải chuỗi rỗng

Một nhầm lẫn phổ biến: coi NULL và '' là như nhau. Chúng không giống nhau.

'' là một chuỗi thật, chỉ là có 0 ký tự. NULL là sự vắng mặt của giá trị. length('') là 0; length(NULL) thì chính nó là NULL. Và NULL = NULL là NULL, không phải 1: đó chính là lý do IS NULL tồn tại.

Nếu một cột có thể chứa cả '' lẫn NULL, hãy quyết định cái nào nghĩa là "thiếu" và giữ nhất quán. Trộn lẫn hai cái buộc mọi truy vấn phải xử lý hai trường hợp, và chắc chắn sẽ có lúc bạn quên một trường hợp.

NULL trong IN, NOT IN và DISTINCT

Còn vài chỗ khác mà null bất ngờ gây rắc rối.

IN với một danh sách có chứa null có thể cho kết quả đáng ngạc nhiên, nhất là với NOT IN:

Bạn có thể nghĩ sẽ nhận được mọi người có tuổi khác 25. Nhưng bạn chẳng nhận được gì. SQLite triển khai NOT IN (25, NULL) gần giống age <> 25 AND age <> NULL, mà age <> NULL luôn là NULL, nên toàn bộ điều kiện không bao giờ đúng. Cách sửa là lọc null khỏi danh sách (hoặc khỏi cột) trước khi so sánh.

Ngược lại, DISTINCT coi các giá trị null là bằng nhau khi loại bỏ trùng lặp:

Bạn nhận được ba hàng: email của Ada, email của Cleo và một NULL duy nhất (gộp từ Boris và Dan). GROUP BY và UNION cũng vậy: chúng coi các null là một nhóm, ngược hẳn với cách = xử lý. SQL không phải lúc nào cũng nhất quán ở điểm này; nên biết rõ mỗi toán tử nằm ở phía nào.

Danh sách kiểm tra nhanh

  • Kiểm tra giá trị bị thiếu bằng IS NULL / IS NOT NULL. Không bao giờ dùng = NULL.
  • Mọi phép tính, phép nối chuỗi hay phép so sánh chạm tới NULL đều trả về NULL.
  • Dùng COALESCE(a, b, c, ...) để thay null bằng giá trị dự phòng. Dùng IFNULL(a, b) cho cách viết tắt hai đối số.
  • Chuỗi rỗng '' không giống NULL. Ở mỗi cột, hãy chọn một trong hai để biểu thị "thiếu".
  • NOT IN (..., NULL) gần như luôn là bug. Hãy loại null khỏi danh sách trước.

Tiếp theo: sắp xếp kết quả

Khi đã lọc hàng đúng cách, kể cả các hàng null, bước tiếp theo là sắp xếp chúng theo thứ tự hữu ích. ORDER BY là nội dung của trang tiếp theo, và nó có quy tắc riêng về vị trí của null trong kết quả đã sắp xếp.

Câu hỏi thường gặp

Vì sao column = NULL không hoạt động trong SQLite?

Vì NULL nghĩa là "không xác định", và mọi phép so sánh với một giá trị không xác định cũng cho ra không xác định, không phải đúng. Vì vậy WHERE col = NULL không khớp hàng nào, kể cả những hàng mà cột thật sự là null. Hãy dùng WHERE col IS NULL. Với <> cũng vậy: hãy dùng IS NOT NULL.

IFNULL và COALESCE trong SQLite khác nhau thế nào?

IFNULL(a, b) nhận đúng hai đối số và trả về a, trừ khi a là null thì trả về b. COALESCE(a, b, c, ...) nhận số đối số bất kỳ và trả về đối số khác null đầu tiên. IFNULL là cách viết tắt cho hai đối số; COALESCE là dạng tổng quát và dùng được trên hầu hết các cơ sở dữ liệu SQL.

NULL có giống chuỗi rỗng trong SQLite không?

Không. NULL nghĩa là "hoàn toàn không có giá trị", còn '' là một chuỗi có độ dài bằng 0, một giá trị thật và đã biết. '' IS NULL trả về 0 (sai), và length('') là 0 trong khi length(NULL) là NULL. Nếu một cột cho phép cả hai, truy vấn của bạn phải xử lý chúng riêng hoặc chuẩn hóa về một loại.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu