为什么 MySQL 的 LEFT JOIN 在使用 WHERE 子句时返回 "NULL" 记录?

Why MySQL's LEFT JOIN is returning "NULL" records when with WHERE clause?

今天我尝试了一些更复杂的 MySQL 查询,我注意到 MySQL 的 LEFT JOIN 不适用于 WHERE 子句。我的意思是,它会 return 一些记录,但不会 return 右侧为空的记录。

例如,假设我们有表:

albums                                   ; albums_rap
  id artist        title         tracks  ;    id artist    title           rank
---- -------- ---------- --------------- ;  ---- --------- ----- --------------
   1 John Doe     Mix CD              20 ;     3 Mark      CD #7            15
   2 Mark          CD #7              35 ;

当我 运行 这个查询时:

SELECT 
    t1.artist as artist,
    t1.title as title,
    t1.tracks as tracks,
    t2.rank as rank,
FROM
    albums as t1
LEFT JOIN
    albums_rap as t2
ON 
    t1.artist LIKE t2.artist
AND
    t1.title LIKE t2.title
WHERE
    t2.rank != 17

我明白了:

artist title tracks  rank
------ ----- ------ -----
Mark   CD #7     35    15

但是当我在此查询中将 "WHERE" 替换为 "AND" 时,我得到:

artist     title tracks  rank
------ --------- ------ -----
Mark       CD #7     35    15
John Doe  Mix CD     20  NULL

为什么第一个不是 returning 带有 "NULL" 的记录(null 不等于 17...)

我希望你明白我的意思,你会以某种方式向我解释其中的区别。抱歉我的英语不好,这不是我的母语。

左连接条件和 where 条件过滤器不相同。物理连接完成后,数据由 where 子句过滤。如果您查看左连接,它通常会 return 从您的左侧开始每一行 table,但是一旦您有了 where 子句,它将过滤连接的输出,因此结果就像一个内部连接。您需要关注下图左侧的两个图表。

"The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2). The result is NULL in the right side when there is no match."

以上引自w3schools.com

但是,当您在 LEFT JOIN 上放置 WHERE 子句时,SQL 现在将其视为 INNER JOIN 并且:

"The INNER JOIN keyword selects all rows from both tables as long as there is a match between the columns in both tables."

也引用自w3schools.com

TLDR;

将 WHERE 子句引入 LEFT OUTER JOIN 使联接具有 INNER JOIN 的行为

第一个案例:

加入后,记录将被(WHERE 子句)过滤。所以只有 1 个结果。

第二种情况(当您将 WHERE 替换为 AND 时)

将 return 所有加入条目 + 满足第二个条件的条目 (t2.rank != 17)。这就是为什么在这里你得到 2 条记录(一条来自 join + 另一条来自 AND 子句)

一旦了解 SQL 语句的执行顺序或逻辑查询处理阶段,这个问题的解决方案就会变得非常直观。顺序是:-

1. FROM
2. ON
3. OUTER
4. WHERE
5. GROUP BY
6. CUBE | ROLLUP
7. HAVING
8. SELECT
9. DISTINCT
10. ORDER BY
11. TOP

由于 ON 是在 JOIN 的 OUTER(LEFT/RIGHT) 部分(添加 NULL 值行)之前执行的,第一种情况在 rank 列中有具有 NULL 值的行。 在第二种情况下,在执行 OUTER JOIN(此处为 LEFT JOIN)后,根据排名列的值过滤掉行。因此,rank 列中具有 NULL 值的行被过滤掉。

在您的 SQL 查询中可以注意到的一件非常重要的事情是与 NULL 的比较 这个区域需要特别注意,因为当使用 NULL/NON-NULL 值时使用普通算术运算符结果为 NULL(它既不是 TRUE 也不是 FALSE),因为 NULL 意味着没有值可用于比较。此行为在 ANSI SQL-92 标准中定义。(这可以通过在某些 SQL 中关闭 ansi null(实际名称可能不同)参数来覆盖处理器) 因此,您使用 where 子句的 SQL 查询会过滤掉 rank 列中具有 NULL 值的行,由于“17 != NULL”,这似乎违反直觉似乎是 TRUE 前

NULL = NULL 结果NULL/UNKNOWN

17 = NULL 结果NULL/UNKNOWN

17 != NULL 结果 NULL?UNKNOWN

一些有趣的 posts/blogs 可供参考:

http://blog.sqlauthority.com/2009/04/06/sql-server-logical-query-processing-phases-order-of-statement-execution/ http://blog.sqlauthority.com/2009/03/15/sql-server-interesting-observation-of-on-clause-on-left-join-how-on-clause-effects-resultset-in-left-join/ http://www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/