SQL LEFT如何与compositie join条件一起工作

时间:2016-09-28 18:48:14

标签: mysql sql join left-join outer-join

假设我想选择保持列的组最大值的行,如本文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 s的行,这些行恰好包含了所需的值。不太清楚的是,不满足连接条件的某些行如何包含在结果集中并且其中包含NULL s,而根本不包含其他行。 如果我删除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以这种方式工作:首先执行交叉连接,然后根据连接条件过滤结果。对于外连接,我不确定。所以我无法理解为什么,例如

之类的行
|    0001 | A      |  3.45 |    0001 | A      |  3.45 |

未包含在上一个查询的结果集中(即添加s1.price < s2.price时)

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

NULL包含在下半场。那么有人可以解释SQL JOIN的行为吗?

3 个答案:

答案 0 :(得分:1)

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

您还应该记住,条件运算符如=,&gt;如果至少有一个参数为NULL

,则总是返回false

所以行

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

未添加,因为它与您的子句s1.price&lt;不匹配s2.price

以及行

|    0001 | B      |  3.99 |    0001 | A      |  3.45 |
只有在您不检查s1.price&lt;之类的价格时才会添加

。 s2.price,仅使用文章加入。

答案 1 :(得分:0)

如果两个表之间的所有行匹配,则左连接和内连接之间没有区别..这是为了查询而没有s1.price&lt; s2.price

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

对于你的问题

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

所以不符合过滤条件

答案 2 :(得分:0)

你必须记住一个JOIN子句,无论实际术语多么复杂,归结为一个简单的布尔值true(加入记录)或false(不可能加入)。

这意味着你的自我加入:

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

会失败,因为3.99不小于3.45。这在连接的“右侧”没有留下匹配的记录。但是,由于你正在进行LEFT加入,这正是你得到的:左边的所有记录,右边有任何匹配的记录 - 如果右边没有匹配的记录,你只需获得{{1} “右”表字段的值。