对于那些刚接触 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));
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中文网其他相关文章!