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 JOIN
s 是这样工作的:首先执行交叉连接,然后根据连接条件过滤结果。对于外部连接,我不确定。所以我不明白为什么,例如,像
这样的行
| 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,仅使用文章加入。
假设我想 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 JOIN
s 是这样工作的:首先执行交叉连接,然后根据连接条件过滤结果。对于外部连接,我不确定。所以我不明白为什么,例如,像
| 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,仅使用文章加入。