INNER JOIN 和 LEFT SEMI JOIN 的区别

Posted

技术标签:

【中文标题】INNER JOIN 和 LEFT SEMI JOIN 的区别【英文标题】:Difference between INNER JOIN and LEFT SEMI JOIN 【发布时间】:2014-03-11 10:03:26 【问题描述】:

INNER JOINLEFT SEMI JOIN 有什么区别?

在下面的场景中,为什么我会得到两个不同的结果?

INNER JOIN 结果集要大得多。有人可以解释吗?我正在尝试获取仅出现在 table_2 中的 table_1 中的名称。

SELECT name
FROM table_1 a
    INNER JOIN table_2 b ON a.name=b.name

SELECT name
FROM table_1 a
    LEFT SEMI JOIN table_2 b ON (a.name=b.name)

【问题讨论】:

内连接将实现您的目标。在看到这个问题之前,我从未听说过半联接。 left semi join 应该返回比inner join 更多的行。 inner join 仅在两个表之间存在匹配时才会返回数据。无论是否在第二个表中找到匹配记录,left join 都会从第一个表返回数据。 @GordonLinoff 不一定,LEFT SEMI JOIN 只会从左边返回一行,即使右边有多个匹配项。如果右侧有多个匹配,INNER JOIN 将返回多行。 @j03z 这不正确。如果左半连接的目的是 1)只返回左表中的信息(正如其他人所说)和 2)无论匹配如何都从左表返回行(正如我认为你所说的那样),那么那就是只是原来的左表——不需要连接来完成它。我认为其他人必须是正确的,即左半连接 1) 仅返回左表中的列,2) 仅返回在右表中匹配的行,以及 3) 将从左侧返回单行或更多匹配项。 【参考方案1】:

INNER JOIN 可以从两个表的列中返回数据,并且可以在任一侧具有多个匹配项的重复记录值。 LEFT SEMI JOIN 只能返回左侧表中的列,并从左侧表中产生一条记录,其中右侧表中有一个或多个匹配项(无论匹配项的数量如何)。它相当于(在标准 SQL 中):

SELECT name
FROM table_1 a
WHERE EXISTS(
    SELECT * FROM table_2 b WHERE (a.name=b.name))

如果右侧列中有 多个 匹配行,INNER JOIN 将为右侧表中的每个匹配返回一行,而 LEFT SEMI JOIN 仅返回来自左表,无论右侧匹配的行数如何。这就是您在结果中看到不同数量的行的原因。

我正在尝试获取 table_1 中仅出现在 table_2 中的名称。

那么LEFT SEMI JOIN 是合适的查询。

【讨论】:

真的有LEFT SEMI JOIN这样的东西吗?不只是一个SEMI JOIN吗? RIGHT SEMI JOIN 没有意义,是吗? 在Hive,是的。 很好的答案正是我想要的。我会更准确地说出答案:“...INNER JOIN 将为每个匹配的右表行返回一行,而 LEFT SEMI JOIN... 与此相反的是LEFT ANTI JOIN,它根据一个key在左表中过滤掉右表中的数据。我想我会把这个金块留给可能正在寻找的人!【参考方案2】:

假设有 2 个表 TableA 和 TableB,只有 2 列(Id、Data)和以下数据:

表A:

+----+---------+
| Id |  Data   |
+----+---------+
|  1 | DataA11 |
|  1 | DataA12 |
|  1 | DataA13 |
|  2 | DataA21 |
|  3 | DataA31 |
+----+---------+

表B:

+----+---------+
| Id |  Data   |
+----+---------+
|  1 | DataB11 |
|  2 | DataB21 |
|  2 | DataB22 |
|  2 | DataB23 |
|  4 | DataB41 |
+----+---------+

Inner JoinId 将返回两个表中的列,并且只返回匹配的记录:

.----.---------.----.---------.
| Id |  Data   | Id |  Data   |
:----+---------+----+---------:
|  1 | DataA11 |  1 | DataB11 |
:----+---------+----+---------:
|  1 | DataA12 |  1 | DataB11 |
:----+---------+----+---------:
|  1 | DataA13 |  1 | DataB11 |
:----+---------+----+---------:
|  2 | DataA21 |  2 | DataB21 |
:----+---------+----+---------:
|  2 | DataA21 |  2 | DataB22 |
:----+---------+----+---------:
|  2 | DataA21 |  2 | DataB23 |
'----'---------'----'---------'
Id 上的

左连接(或左外连接)将返回表中的列以及与左表中的记录匹配的记录(右表中的空值):

.----.---------.----.---------.
| Id |  Data   | Id |  Data   |
:----+---------+----+---------:
|  1 | DataA11 |  1 | DataB11 |
:----+---------+----+---------:
|  1 | DataA12 |  1 | DataB11 |
:----+---------+----+---------:
|  1 | DataA13 |  1 | DataB11 |
:----+---------+----+---------:
|  2 | DataA21 |  2 | DataB21 |
:----+---------+----+---------:
|  2 | DataA21 |  2 | DataB22 |
:----+---------+----+---------:
|  2 | DataA21 |  2 | DataB23 |
:----+---------+----+---------:
|  3 | DataA31 |    |         |
'----'---------'----'---------'
Id 上的

右连接(或右外连接)将返回表中的列以及与右表中的记录匹配的记录(左表中的空值):

┌────┬─────────┬────┬─────────┐
│ Id │  Data   │ Id │  Data   │
├────┼─────────┼────┼─────────┤
│  1 │ DataA11 │  1 │ DataB11 │
│  1 │ DataA12 │  1 │ DataB11 │
│  1 │ DataA13 │  1 │ DataB11 │
│  2 │ DataA21 │  2 │ DataB21 │
│  2 │ DataA21 │  2 │ DataB22 │
│  2 │ DataA21 │  2 │ DataB23 │
│    │         │  4 │ DataB41 │
└────┴─────────┴────┴─────────┘
Id 上的

完全外连接 将返回表中的列以及与左表中的记录(右表中的空值)和右表中的记录(左表中的空值)匹配的记录表):

╔════╦═════════╦════╦═════════╗
║ Id ║  Data   ║ Id ║  Data   ║
╠════╬═════════╬════╬═════════╣
║  - ║         ║    ║         ║
║  1 ║ DataA11 ║  1 ║ DataB11 ║
║  1 ║ DataA12 ║  1 ║ DataB11 ║
║  1 ║ DataA13 ║  1 ║ DataB11 ║
║  2 ║ DataA21 ║  2 ║ DataB21 ║
║  2 ║ DataA21 ║  2 ║ DataB22 ║
║  2 ║ DataA21 ║  2 ║ DataB23 ║
║  3 ║ DataA31 ║    ║         ║
║    ║         ║  4 ║ DataB41 ║
╚════╩═════════╩════╩═════════╝

Left Semi Join Id 列将仅返回左表中的列,仅返回左表中的匹配记录:

┌────┬─────────┐
│ Id │  Data   │
├────┼─────────┤
│  1 │ DataA11 │
│  1 │ DataA12 │
│  1 │ DataA13 │
│  2 │ DataA21 │
└────┴─────────┘

【讨论】:

我以前称之为“LEFT INNER Join”。 DISTINCT of A.* from INNER JOIN 结果等价于 LEFT SEMI JOIN。 Distinct 听起来不安全,假设 A 包含两条相同的记录。 即使结果最终相同,与 EXISTS 相比,使用 DISTINCT 的计划成本可能更高【参考方案3】:

在 Hive 中尝试并得到以下输出

表1

1,wqe,钦奈,印度

2,斯图,塞勒姆,印度

3,米娅,班加罗尔,印度

4,yepie,纽约,美国

表2

1,wqe,钦奈,印度

2,斯图,塞勒姆,印度

3,米娅,班加罗尔,印度

5,chapie,洛杉矶,美国

内联

SELECT * FROM table1 INNER JOIN table2 ON (table1.id = table2.id);

1 wqe 印度钦奈 1 wqe 钦奈印度

2 斯图塞勒姆印度 2 斯图塞勒姆印度

3 mia 印度班加罗尔 3 mia 印度班加罗尔

左连接

SELECT * FROM table1 LEFT JOIN table2 ON (table1.id = table2.id);

1 wqe 印度钦奈 1 wqe 钦奈印度

2 斯图塞勒姆印度 2 斯图塞勒姆印度

3 mia 印度班加罗尔 3 mia 印度班加罗尔

4 yepie newyork USA NULL NULL NULL NULL

左半连接

SELECT * FROM table1 LEFT SEMI JOIN table2 ON (table1.id = table2.id);

1 wqe 印度钦奈

2 斯图塞勒姆印度

3 mia 印度班加罗尔

注意:只显示左表中的记录,而左连接显示两个表记录

【讨论】:

【参考方案4】:

以上所有答案都是正确的。然而在实践中,在想象 LEFT SEMI JOIN 时,关联filter 的心智模型会有所帮助。

答案是 LEFT 表中的行子集,它们在 RIGHT TABLE 中有匹配项。

【讨论】:

以上是关于INNER JOIN 和 LEFT SEMI JOIN 的区别的主要内容,如果未能解决你的问题,请参考以下文章

SQL中left join on 、right join on、inner join on之间的区别

求助,left join union right join一直报错

R语言dplyr包进行dataframe的连接(inner_joinleft_joinright_joinfull_joinsemi_joinanti_join)操作实战

面试官:说说left join和left semi join 两者有什么区别?

hive 的 left semi join

Python threading 中join()的作用