首頁  >  文章  >  資料庫  >  MySQL 中空值的複雜性

MySQL 中空值的複雜性

PHPz
PHPz原創
2024-07-17 09:48:49449瀏覽

The Intricacies of Null Values in MySQL

介紹:

對於那些剛接觸 MySQL 的人,包括我自己,當您從包含一些空值的表中查詢時,您可能會遇到意想不到的結果。在 MySQL 中,null 既不是 true 也不是 false,而是未知的,除非使用「IS NULL」或「IS NOT NULL」運算符,否則無法將 null 與 null 進行比較。我發現 MySQL 處理 null 的方式不僅有趣,而且理解起來也很重要。在這裡,我將使用各種 MySQL 函數來示範 null 的行為!

連接表與包含空值的欄位

INNER JOIN 的 NULL 與 LEFT JOIN 的 NULL
假設有兩個表,如下所述:

表名:教師

id dept_id name
101 1 Shrivell
102 1 Throd
103 1 Splint
104 NULL Spiregrain
105 2 Cutflower
106 NULL Deadyawn

表名稱:部門

id name
1 Computing
2 Design
3 Engineering
NULL NULL

請注意,教師表中存在空值。如果我使用 INNER JOIN 組合這兩個表會發生什麼?

SELECT teachers.name, departments.name AS department
FROM teachers INNER JOIN departments
ON (teachers.dept_id = departments.id)

這是結果

+-----------+------------+
| Name      | Department |
+-----------+------------+
| Shrivell  | Computing  |
| Throd     | Computing  |
| Splint    | Computing  |
| Cutflower | Design     |
+-----------+------------+

請注意,在結果中,您看不到部門 ID 為空的兩位教師的姓名。

但是使用 LEFT JOIN 呢?我們來看一下。
在下面的查詢中,我只需將“INNER”替換為“LEFT”。

SELECT teachers.name, departments.name AS department
FROM teachers LEFT JOIN departments
ON (teachers.dept_id = departments.id)

這是結果

+------------+------------+
| Name       | Department |
+------------+------------+
| Shrivell   | Computing  |
| Throd      | Computing  |
| Splint     | Computing  |
| Spiregrain | NULL       |
| Cutflower  | Design     |
| Deadyawn   | NULL       |
+------------+------------+

您會注意到所有老師都已列出。 INNER JOIN 不會傳回具有空值的記錄,但 LEFT JOIN 會回傳。

為什麼?

請記住,當使用等於運算子 (=) 和 NULL 到 NULL 時,結果既不是 true 也不是 false — 這是未知的。因此,使用 INNER JOIN 時,比較兩邊都有 NULL 值的記錄不會包含在結果集中。但是,根據 LEFT JOIN 的性質,它會從左側表(本例中為教師表)中選擇所有記錄,即使 dept_id 為 null。與 INNER JOIN 不同,INNER JOIN 會從清單中刪除此類記錄,而 LEFT JOIN 會保留它們。

EXISTS & NULL 與 IN & NULL

EXISTS 和 IN 都不會傳回空值記錄。
讓我們考慮一下我有這些表格的範例。

表名稱:customers

customer_id first_name last_name phone_number
1 John Doe 123-456-7890
2 Jane Smith 987-654-3210
3 Alice Johnson NULL
4 Bob Brown 555-555-5555

表格名稱:customers_2

customer_id first_name last_name phone_number
1 John Doe 123-456-7890
2 Jane Smith NULL
3 Alice Johnson 111-222-3333
4 Bob Brown NULL

Here is a query:
Using EXISTS to check if a name in b_table exists in a_table.

SELECT * FROM customers AS c1
WHERE EXISTS(
SELECT * FROM customers_2 AS c2
WHERE c1.first_name = c2.first_name 
AND c1.last_name = c2.last_name 
AND c1.phone_number = c2.phone_number); 

Using IN to see if name in b_table is in a_table

SELECT *
FROM customers AS c1
WHERE (c1.first_name, c1.last_name, c1.phone_number) IN (
    SELECT c2.first_name, c2.last_name, c2.phone_number
    FROM customers_2 AS c2
);

Both returns the same result

+-------------+------------+-----------+--------------+
| customer_id | first_name | last_name | phone_number |
+-------------+------------+-----------+--------------+
| 1           | John       | Doe       | 123-456-7890 |
+-------------+------------+-----------+--------------+

Notice that records with null values are not returned.

WHY?
Because with EXISTS, you can't link columns that are null. For IN, it ignores null values as if they don't exist. Thus, both treat nulls in the same way: neither returns nulls.

** HOWEVER, it's not the same story with NOT EXISTS and NOT IN when dealing with NULL!**

Let's take a look.
I simply swapped EXISTS for NOT EXISTS.

SELECT * FROM customers AS c1
WHERE NOT EXISTS(
SELECT * FROM customers_2 AS c2
WHERE c1.first_name = c2.first_name 
AND c1.last_name = c2.last_name 
AND c1.phone_number = c2.phone_number);

Here is the result

| customer_id | first_name | last_name | phone_number  |
|-------------|------------|-----------|---------------|
| 2           | Jane       | Smith     | 987-654-3210  |
| 3           | Alice      | Johnson   | [NULL]        |
| 4           | Bob        | Brown     | 555-555-5555  |

Notice that it is returning records with null values. Both Jane and Bob's phone numbers in the customers_2 table are null, but since both phone numbers have values in the customers table, the returned values are not null.

How about using NOT IN?

SELECT *
FROM customers AS c1
WHERE (c1.first_name, c1.last_name, c1.phone_number) NOT IN (
    SELECT c2.first_name, c2.last_name, c2.phone_number
    FROM customers_2 AS c2
);

Here is the result

+-------------+------------+-----------+--------------+
| customer_id | first_name | last_name | phone_number |
+-------------+------------+-----------+--------------+

Nothing is returned!
Why does NOT EXISTS return null values but NOT IN does not? It's because they both treat null differently. NOT EXISTS is simply the opposite of EXISTS. It returns EVERYTHING that is not returned by EXISTS, including nulls.

However, NOT IN returns records that evaluate to TRUE, but in MySQL, null is neither TRUE nor FALSE—it is unknown.

Let's take a look at the record for Jane Smith with Id2.
When NOT IN is used, it compares:
Jane != Jane OR Smith != Smith OR 987-654-3210 != null.

Jane != Jane -> False
Smith != Smith -> False
987-654-3210 != null -> NULL
False or False or Null evaluates to NULL, so the record doesn't get returned!

Here's a little twist:
What if the first name in the customers table does not match the first name in the customers_2 table?

Let’s compare:
Jane Smith 987-654-3210 (from the customers table) and Jack Smith null (from the customers_2 table).

Here is the result

+-------------+------------+-----------+--------------+
| customer_id | first_name | last_name | phone_number |
+-------------+------------+-----------+--------------+
| 2           | Jack       | Smith     | 987-654-3210 |

What? You see Jack! Doesn't NOT IN fail to evaluate null as either false or true, so the record shouldn't be returned?

Let's analyze it:

Jane != Jack -> True
Smith != Smith -> False
987-654-3210 != null -> NULL

True or False or Null actually returns:
True || False -> True
True || NULL -> True
Therefore, the record gets returned!

If you want to understand how true, false, and null are evaluated, here is the link:

https://dev.mysql.com/doc/refman/8.0/en/logical-operators.html#operator_and

As you can see, it can get quite confusing when it comes to comparing null values.

At the end of the day, I think it's best to explicitly compare null values using IS NULL or IS NOT NULL, which returns true or false as shown below.

SELECT * FROM customers AS c1
WHERE EXISTS(
SELECT * FROM customers_2 AS c2
WHERE c1.first_name = c2.first_name 
AND c1.last_name = c2.last_name 
AND c1.phone_number = c2.phone_number OR  (c1.phone_number IS NULL AND c2.phone_number IS NULL));

Conclusion

Handling null values in MySQL can be quite challenging, requiring careful attention when they are present in your database. It's crucial to conduct explicit null checking to clearly define how null values should be treated. By implementing the strategies I mentioned earlier, such as using IS NULL or IS NOT NULL, you can ensure more predictable and accurate query results.

以上是MySQL 中空值的複雜性的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn