这里的理论问题是:

为什么要指定表格.字段为空或为表.字段不为NULL不适用于连接条件(例如左或右连接),但仅适用于where条件?

非工作示例:

-这将返回所有装运,并过滤掉所有退货(非空值).但是,无论是否有任何内容符合[r.id为null]语句,它都会返回所有装运.

SELECT
  *
FROM 
  shipments s
LEFT OUTER JOIN returns r  
  ON s.id = r.id
  AND r.id is null
WHERE
  s.day >= CURDATE() - INTERVAL 10 DAY 

工作示例:

-这将返回正确的行数,即总发货量减go 与退货相关的任何行数(非空值).

SELECT
  *
FROM 
  shipments s
LEFT OUTER JOIN returns r  
  ON s.id = r.id
WHERE
  s.day >= CURDATE() - INTERVAL 10 DAY
  AND r.id is null

为什么会这样?连接的两个表之间的所有其他筛选条件都可以正常工作,但由于某些原因,除非在where语句中,否则IS NULL和IS NOT NULL筛选器不工作.

原因是什么?

推荐答案

表A和表B的示例:

 A (parent)       B (child)    
============    =============
 id | name        pid | name 
------------    -------------
  1 | Alex         1  | Kate
  2 | Bill         1  | Lia
  3 | Cath         3  | Mary
  4 | Dale       NULL | Pan
  5 | Evan  

如果你想找到父母和他们的子元素,你可以做INNER JOIN:

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  INNER JOIN  child
  ON   parent.id     =    child.pid

结果是,左表中的parent's id和第二表中的child's pid的每个匹配项都将在结果中显示为一行:

+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  1 | Alex   |   1  | Kate  |
|  1 | Alex   |   1  | Lia   |
|  3 | Cath   |   3  | Mary  |
+----+--------+------+-------+

现在,上面没有显示没有子元素的家长(因为他们的ID在子元素的ID中没有匹配项,所以你怎么办?你做一个外部联接.有三种类型的外部联接,左、右和完整的外部联接.我们需要左一个,因为我们想要左表(家长)中的"额外"行:

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  LEFT JOIN  child
  ON   parent.id    =    child.pid

结果是,除了之前的匹配之外,所有没有匹配的父母(阅读:不要有子元素)也会显示:

+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  1 | Alex   |   1  | Kate  |
|  1 | Alex   |   1  | Lia   |
|  3 | Cath   |   3  | Mary  |
|  2 | Bill   | NULL | NULL  |
|  4 | Dale   | NULL | NULL  |
|  5 | Evan   | NULL | NULL  |
+----+--------+------+-------+

NULL人是从哪里来的?好吧,MySQL(或您可能使用的任何其他RDBMS)将不知道该放在那里什么,因为这些父母没有匹配项(kid),所以没有pidchild.name可与这些父母匹配.所以,它把这个特殊的非值称为NULL.

My point is that these 100 are created (in the result set) during the 101.


所以,如果我们只想显示没有子元素的父母,我们可以在上面的LEFT JOIN加上WHERE child.pid IS NULL.The 102 clause is evaluated (checked) after the 103完成了.因此,从上面的结果可以清楚地看出,只有pid为空的最后三行才会显示:

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  LEFT JOIN  child
  ON   parent.id    =    child.pid

WHERE child.pid IS NULL

结果:

+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  2 | Bill   | NULL | NULL  |
|  4 | Dale   | NULL | NULL  |
|  5 | Evan   | NULL | NULL  |
+----+--------+------+-------+

现在,如果我们把IS NULL支票从WHERE条款移到ON条款,会发生什么?

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  LEFT JOIN  child
  ON   parent.id    =    child.pid
  AND  child.pid IS NULL

在这种情况下,数据库try 从两个表中查找符合这些条件的行.也就是parent.id = child.pid AND child.pid IN NULL所在的行.但它可以找到no such match,因为child.pid可以等于某个值(1、2、3、4或5),同时为空!

所以,条件是:

ON   parent.id    =    child.pid
AND  child.pid IS NULL

相当于:

ON   1 = 0

总是False.

那么,为什么它会返回左表中的所有行呢?Because it's a LEFT JOIN!和left连接返回rows that match (none in this case)rows from the left table that do not match支票(all in this case):

+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  1 | Alex   | NULL | NULL  |
|  2 | Bill   | NULL | NULL  |
|  3 | Cath   | NULL | NULL  |
|  4 | Dale   | NULL | NULL  |
|  5 | Evan   | NULL | NULL  |
+----+--------+------+-------+

我希望上面的解释是清楚的.



旁注(与你的问题没有直接关系):为什么Pan在我们的加入中都没有出现?因为他的pidNULL,在SQL的(非公共)逻辑中NULL不等于任何值,所以它不能与任何父ID(1、2、3、4和5)匹配.即使有一个空值,它仍然不会匹配,因为NULL不等于任何东西,甚至NULL本身也不等于任何东西(这的确是一个非常奇怪的逻辑!).这就是为什么我们使用IS NULL号特别支票而不是= NULL号支票.

如果我们做RIGHT JOINPan会出现吗?是的,会的!因为右连接将显示所有匹配的结果(我们做的第一个内部连接)加上右表中所有不匹配的行(在我们的例子中是一行,(NULL, 'Pan')行).

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  RIGHT JOIN  child
  ON   parent.id     =    child.pid

结果:

+------+--------+------+-------+
| id   | parent | pid  | child | 
+---------------+------+-------+
|   1  | Alex   |   1  | Kate  |
|   1  | Alex   |   1  | Lia   |
|   3  | Cath   |   3  | Mary  |
| NULL | NULL   | NULL | Pan   |
+------+--------+------+-------+

不幸的是,MySQL没有FULL JOIN个.您可以在其他RDBMS中try ,它将显示:

+------+--------+------+-------+
|  id  | parent | pid  | child | 
+------+--------+------+-------+
|   1  | Alex   |   1  | Kate  |
|   1  | Alex   |   1  | Lia   |
|   3  | Cath   |   3  | Mary  |
|   2  | Bill   | NULL | NULL  |
|   4  | Dale   | NULL | NULL  |
|   5  | Evan   | NULL | NULL  |
| NULL | NULL   | NULL | Pan   |
+------+--------+------+-------+

Mysql相关问答推荐

如何将MySQL与AS&Quot;语法一起用于存储过程返回的表?

JOOQ-如何在一个查询中引用多个(但不是所有)表中的所有字段

根据SQL中的平均范围对学生进行排名

子查询是否可以按主查询中的列进行分组?

mysqli 无法以非 root 用户身份连接

Group By 查询运行速度太慢而无法正常运行

无法从mysql数据库获取数据

如何清除mysql 8 innodb中的数据库缓存

使用索引进行查询优化

Mysql如何分行分词

如何根据 R 中的价格范围将数据从一列复制到新列?

Mysql,显示谁已经和没有 Select 退出巴士服务

mysql数据库数据文件夹中的DESKTOP-7JFP5MF-bin.000001文件有什么用?

MySQL 1292 截断不正确的日期时间值:'2020-09-11T08:32-50Z'

MySQL表中给定字段的每个不同值的连续记录形成对

在 MySQL 中转换 JSON 数组值

减少mysql中的值但不是负数

将mysql查询输出存储到shell变量中

匹配 IN 子句中的所有值

AWS RDS 实例升级停机时间