【问题标题】:Why MySQL's LEFT JOIN is returning "NULL" records when with WHERE clause?为什么 MySQL 的 LEFT JOIN 在使用 WHERE 子句时返回“NULL”记录?
【发布时间】:2015-02-18 17:08:20
【问题描述】:

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

例如,假设我们有表格:

专辑;专辑说唱
  id艺术家标题曲目; id艺人头衔排名
---- -------- ---------- --------------- ; ---- --------- ----- --------------
   1 张约翰·多伊混合 CD 20 ; 3 标记 CD #7 15
   2 标记 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

我明白了:

艺人主打歌排名
------ ----- ------ -----
标记 CD #7 35 15

但是当我在这个查询中用“AND”替换“WHERE”时,我得到:

艺人主打歌排名
------ --------- ------ -----
标记 CD #7 35 15
John Doe Mix CD 20 NULL

为什么第一个不返回带有“NULL”的记录(null 不等于 17...)

我希望你明白我的意思,你会以某种方式解释我的不同之处。抱歉我的英语不好,这不是我的母语。

【问题讨论】:

  • 左连接条件为内连接。
  • 您确实需要重新考虑您的餐桌设计。对于初学者来说,将艺术家作为字符串存储在专辑表中是一个非常糟糕的主意。如果他们都被称为“约翰史密斯”,你如何区分他们?您应该有一个单独的艺术家表并将艺术家 ID 存储在专辑表中。此外,为每种类型设置单独的表格也很糟糕。改为使用流派 ID 列
  • @GordonM:这只是一个例子,我的桌子看起来一点也不像,但我想不出更好更简单的。无论如何,在专辑表中包含所有艺术家和 ID 的表是个好主意,谢谢!

标签: php mysql sql left-join where


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    一旦知道了 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
    

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

    在您的 SQL 查询中可以注意到的一件非常重要的事情是与 NULL 的比较 这个区域需要特别注意,因为当使用正常算术运算符使用 NULL/NON-NULL 值时,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

    一些有趣的帖子/博客供参考:

    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/

    【讨论】:

    • 这是最有用的贡献
    【解决方案3】:

    第一种情况:

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

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

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

    【讨论】:

    • 对于每个匹配左连接表的 where 条件将 where 条件向上移动到左连接语句并使用 AND
    【解决方案4】:

    "LEFT JOIN 关键字返回左表(table1)中的所有行,以及右表(table2)中的匹配行。如果没有匹配,则右侧为NULL >。”

    以上引用来自w3schools.com

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

    “只要两个表中的列之间存在匹配,INNER JOIN 关键字就会选择两个表中的所有行。”

    也引用自w3schools.com

    TLDR;

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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-14
    • 2011-07-06
    • 2021-02-15
    • 1970-01-01
    相关资源
    最近更新 更多