SQL 具有复合连接条件的 LEFT JOIN 如何工作

How SQL LEFT JOINs with compositie join conditions work

假设我想 select 像本文 http://dev.mysql.com/doc/refman/5.7/en/example-maximum-column-group-row.html 中描述的那样保存列的分组最大值的行。它确实解决了问题。但是我不明白最后一个解决方案是如何工作的。如果我执行了描述的所有步骤,我会得到想要的结果:

CREATE TABLE shop (
    article INT(4) UNSIGNED ZEROFILL DEFAULT '0000' NOT NULL,
    dealer  CHAR(20)                 DEFAULT ''     NOT NULL,
    price   DOUBLE(16,2)             DEFAULT '0.00' NOT NULL,
    PRIMARY KEY(article, dealer));

INSERT INTO shop VALUES
(1,'A',3.45),(1,'B',3.99),(2,'A',10.99),(3,'B',1.45),
(3,'C',1.69),(3,'D',1.25),(4,'D',19.95);

SELECT s1.*
FROM shop s1
LEFT JOIN shop s2 ON s1.article = s2.article AND s1.price < s2.price
WHERE s2.price IS NULL;

+---------+--------+-------+
| article | dealer | price |
+---------+--------+-------+
|    0001 | A      |  3.45 |
|    0001 | B      |  3.99 |
|    0002 | A      | 10.99 |
|    0003 | B      |  1.45 |
|    0003 | C      |  1.69 |
|    0003 | D      |  1.25 |
|    0004 | D      | 19.95 |
+---------+--------+-------+

但我不明白这个结果是如何使用 LEFT JOIN 和复合连接条件获得的。如果我在没有 WHERE 子句的情况下尝试相同的查询,我会得到:

SELECT *
FROM shop s1
LEFT JOIN shop s2 ON s1.article = s2.article AND s1.price < s2.price;

+---------+--------+-------+---------+--------+-------+
| article | dealer | price | article | dealer | price |
+---------+--------+-------+---------+--------+-------+
|    0001 | A      |  3.45 |    0001 | B      |  3.99 |
|    0001 | B      |  3.99 |    NULL | NULL   |  NULL |
|    0002 | A      | 10.99 |    NULL | NULL   |  NULL |
|    0003 | B      |  1.45 |    0003 | C      |  1.69 |
|    0003 | C      |  1.69 |    NULL | NULL   |  NULL |
|    0003 | D      |  1.25 |    0003 | B      |  1.45 |
|    0003 | D      |  1.25 |    0003 | C      |  1.69 |
|    0004 | D      | 19.95 |    NULL | NULL   |  NULL |
+---------+--------+-------+---------+--------+-------+

显然,第一个查询只获取了 NULL 恰好包含所需值的行。不太清楚的是,某些不满足连接条件的行如何包含在结果集中并在其中包含 NULL,而其他行根本不包含。 如果我删除 s1.price < s2.price 条件,结果如下所示:

SELECT *
FROM shop s1
LEFT JOIN shop s2 ON s1.article = s2.article;

+---------+--------+-------+---------+--------+-------+
| article | dealer | price | article | dealer | price |
+---------+--------+-------+---------+--------+-------+
|    0001 | A      |  3.45 |    0001 | A      |  3.45 |
|    0001 | A      |  3.45 |    0001 | B      |  3.99 |
|    0001 | B      |  3.99 |    0001 | A      |  3.45 |
|    0001 | B      |  3.99 |    0001 | B      |  3.99 |
|    0002 | A      | 10.99 |    0002 | A      | 10.99 |
|    0003 | B      |  1.45 |    0003 | B      |  1.45 |
|    0003 | B      |  1.45 |    0003 | C      |  1.69 |
|    0003 | B      |  1.45 |    0003 | D      |  1.25 |
|    0003 | C      |  1.69 |    0003 | B      |  1.45 |
|    0003 | C      |  1.69 |    0003 | C      |  1.69 |
|    0003 | C      |  1.69 |    0003 | D      |  1.25 |
|    0003 | D      |  1.25 |    0003 | B      |  1.45 |
|    0003 | D      |  1.25 |    0003 | C      |  1.69 |
|    0003 | D      |  1.25 |    0003 | D      |  1.25 |
|    0004 | D      | 19.95 |    0004 | D      | 19.95 |
+---------+--------+-------+---------+--------+-------+

据我所知,INNER JOINs 是这样工作的:首先执行交叉连接,然后根据连接条件过滤结果。对于外部连接,我不确定。所以我不明白为什么,例如,像

这样的行
|    0001 | A      |  3.45 |    0001 | A      |  3.45 |

根本不包含在先前查询的结果集中(即添加 s1.price < s2.price 时),而

|    0001 | B      |  3.99 |    0001 | A      |  3.45 |

在下半场与 NULL 一起被收录。那么有人可以解释 SQL JOIN 的行为吗?

如果两者之间的所有行都匹配 table 则左连接和内连接之间没有区别..这是为您查询而没有 s1.price < s2.price

当两个 table 之间的条件基于不匹配的值时,左连接很有用..然后您在相关字段中有空值.. 使用此功能,条件如 s1.price < s2.price(当行匹配 s1.article = s2.article 时)将 s1.price 的值设置为 null 当此值较小时然后 s2.article 的所有值 .. 然后用于获取最大值

针对你的问题

    0001 | A      |  3.45 |    0001 | A      |  3.45 |
    0001 | B      |  3.99 |    0001 | A      |  3.45 |
                   ^^ s1.price is not <   of    ^^ s2.price

所以不符合过滤条件

您必须记住,无论实际术语多么复杂,JOIN 子句都可以归结为简单的布尔值 true(连接记录)或 false(无法连接)。

这意味着你自己加入:

1/b/3.99 -> 1/a/3.45

会失败,因为 3.99 不小于 3.45。这在连接的 "right" 端没有留下任何匹配记录。但是因为你正在做一个 LEFT 连接,这正是你得到的:左边的所有记录,右边有任何匹配的记录 - 如果右边没有匹配的记录,你只需得到 null "right" table 字段的值。

通过使用左连接,您始终可以从左侧获取所有行 table(在您的示例中为 s1)。 当 ON 子句条件匹配来自 s1 的特定行时,连接结果将包含它与 s2 匹配的次数 之后 where 条件适用于结果集(如果有的话)

您还应该记住,如果至少有一个参数为 NULL

,条件运算符如 =、> 总是 return false

所以行

|    0001 | A      |  3.45 |    0001 | A      |  3.45 |

未添加,因为它与您的条款不匹配 s1.price < s2.price

以及行

|    0001 | B      |  3.99 |    0001 | A      |  3.45 |

被添加,仅当您不检查价格时 s1.price < s2.price,仅使用文章加入。