Nói zìa mấy thứ linh tinh liên quan tới NULL trong SQL.

Lược dịch + thêm ví dụ từ https://mitchum.blog

1. NULL

CREATE TABLE `t` (
  `a` varchar(10) DEFAULT NULL,
  `b` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO t(a) VALUES ('1');
INSERT INTO t(b) VALUES ('2');
INSERT INTO t VALUES ();

Câu hỏi là nếu có 2 truy vấn như sau thì nên sử dụng truy vấn nào?

SELECT * FROM t WHERE b = NULL;

SELECT * FROM t WHERE b IS NULL;

Câu trả lời cuối cùng là nên dùng IS, sách mô tả như sau:

In SQL, NULL represents the concept of “unknown”

So sánh sự khác biệt giữa NULL và empty value, dữ liệu thể hiện như sau:

INSERT INTO t VALUES ('', '');
SELECT * FROM t;'
+------+------+
| a    | b    |
+------+------+
| 1    | NULL |
| NULL | 2    |
| NULL | NULL |
|      |      |
+------+------+
4 rows in set (0.00 sec)

3. Unknown?

Biểu thức WHERE 1 = 1 trả về giá trị là true, truy vấn sẽ trả về toàn bộ dữ liệu của bảng.

SELECT * FROM t WHERE 1 = 1;

Biểu thức WHERE 1 = 0 trả về giá trị là false, nghĩa là truy vấn sẽ không trả về bất kỳ dữ liệu nào.

SELECT * FROM t WHERE 1 = 0;

Còn nếu có biểu thức WHERE 1 = NULL thì do không biết kết quả của phép so sánh là gì, nên truy vấn cũng không trả về bất cứ dữ liệu gì.

SELECT * FROM t WHERE 1 = NULL;

Còn nếu điều kiện là NULL thì cũng không trả về bất cứ dữ liệu gì.

SELECT * FROM t WHERE NULL;

Túm lại thì:

  • Biểu thức sau WHEREtrue thì trả về dữ liệu.
  • Biểu thức sau WHEREfalse thì không trả về dữ liệu.
  • Hoặc trả về NULL hoặc unknown thì cũng không trả về dữ liệu.

Câu hỏi tiếp theo là database xử lý 2 trường hợp falseNULL như nhau thì tại sao phải quan tâm? Tiếp theo thử thêm điều kiện NOT().

SELECT * FROM t WHERE NOT(1 = 0);
SELECT * FROM t WHERE NOT(1 = 1);
  • NOT(false) = true, trả về dữ liệu.
  • NOT(true) = false, không trả về dữ liệu.

Nhưng nếu WHERE NOT(1 = NULL) thì database không trả về true, cũng không trả về false mà trả về một NULL khác ?? 🥺 ?? (đại khái là một NULL khác, khác với cái NULL trước mặc dù đều là NULL =))))))))))

Túm lại thì 2 truy vấn sau trả về kết quả giống y chang nhau, dù điều kiện đối lập nhau.

SELECT * FROM t WHERE 1 = NULL;
SELECT * FROM t WHERE NOT(1 = NULL);

3. NULL and NOT IN

Có điều kiện với INWHERE 1 IN (1, 2, 3, 4, NULL), kết quả của biểu thức là true vì số 1 có tồn tại trong danh sách => trả về dữ liệu của bảng.

SELECT * FROM t WHERE 1 IN (1, 2, 3, 4, NULL);

Nếu điều kiện WHERE 1 NOT IN (1, 2, 3, 4, NULL), kết quả của biểu thức sẽ là false như bình thường vì số 1 có tồn tại trong danh sách.

SELECT * FROM t WHERE 1 NOT IN (1, 2, 3, 4, NULL);

Nhưng vấn đề là nếu với điều kiện 5 NOT IN (1, 2, 3, 4, NULL) cũng sẽ không trả về dữ liệu của bảng, dù số 5 không tồn tại trong danh sách.

SELECT * FROM t WHERE 5 NOT IN (1, 2, 3, 4 NULL);

Postgres wiki cũng có nói tới trường hợp tương tự, họ khuyến cáo là không nên sử dụng NOT IN hoặc bất kỳ truy vấn nào kết hợp của NOTIN ví dụ NOT (x IN (SELECT ...)).

Ngoài ra Markus Winand có một danh sách cách chủ đề chuyên sâu liên quan đến NULL trong SQL có thể tham khảo ở https://modern-sql.com/concept/null hoặc https://modern-sql.com/concept/three-valued-logic.

4. Ref