“INNER JOIN”和“INNER JOIN”有什么区别? 和“外连接”?

发布于 2024-07-05 21:46:58 字数 105 浏览 12 评论 0 原文

另外,LEFT OUTER JOINRIGHT OUTER JOINFULL OUTER JOIN 如何适应?

Also, how do LEFT OUTER JOIN, RIGHT OUTER JOIN, and FULL OUTER JOIN fit in?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(27

差↓一点笑了 2024-07-12 21:46:59

1.内连接:也称为连接。 仅当存在匹配时,它才会返回左表和右表中存在的行。 否则,它返回零记录。

示例:

SELECT
  e1.emp_name,
  e2.emp_salary    
FROM emp1 e1
INNER JOIN emp2 e2
  ON e1.emp_id = e2.emp_id

 output1

2.Full Outer Join:也称为Full Join。 它返回左表和右表中存在的所有行

示例:

SELECT
  e1.emp_name,
  e2.emp_salary    
FROM emp1 e1
FULL OUTER JOIN emp2 e2
  ON e1.emp_id = e2.emp_id

 output2

3.Left Outer join:或者简称为Left Join。 它返回左表中存在的所有行以及右表中的匹配行(如果有)。

4.右外连接:也称为右连接。 它返回左表(如果有)中的匹配行以及右表中存在的所有行。

加入

连接的优点

  1. 执行速度更快。

1.Inner Join: Also called as Join. It returns the rows present in both the Left table, and right table only if there is a match. Otherwise, it returns zero records.

Example:

SELECT
  e1.emp_name,
  e2.emp_salary    
FROM emp1 e1
INNER JOIN emp2 e2
  ON e1.emp_id = e2.emp_id

output1

2.Full Outer Join: Also called as Full Join. It returns all the rows present in both the Left table, and right table.

Example:

SELECT
  e1.emp_name,
  e2.emp_salary    
FROM emp1 e1
FULL OUTER JOIN emp2 e2
  ON e1.emp_id = e2.emp_id

output2

3.Left Outer join: Or simply called as Left Join. It returns all the rows present in the Left table and matching rows from the right table (if any).

4.Right Outer Join: Also called as Right Join. It returns matching rows from the left table (if any), and all the rows present in the Right table.

joins

Advantages of Joins

  1. Executes faster.
猫烠⑼条掵仅有一顆心 2024-07-12 21:46:59

您可以使用 INNER JOIN 返回两个表中存在匹配的所有行。 即在结果表中,所有行和列都将具有值。

OUTER JOIN 中,结果表可能有空列。 外连接可以是LEFTRIGHT

LEFT OUTER JOIN 返回第一个表中的所有行,即使第二个表中没有匹配项也是如此。

RIGHT OUTER JOIN 返回第二个表中的所有行,即使第一个表中没有匹配项也是如此。

You use INNER JOIN to return all rows from both tables where there is a match. i.e. In the resulting table all the rows and columns will have values.

In OUTER JOIN the resulting table may have empty columns. Outer join may be either LEFT or RIGHT.

LEFT OUTER JOIN returns all the rows from the first table, even if there are no matches in the second table.

RIGHT OUTER JOIN returns all the rows from the second table, even if there are no matches in the first table.

叫思念不要吵 2024-07-12 21:46:59

最简单的定义

内连接:从两个表中返回匹配的记录

完全外连接:从两个表中返回匹配和不匹配的记录,对于两个表中不匹配的记录返回 null。

左外连接:仅从左侧的表中返回匹配和不匹配的记录。

右外连接:仅从右侧的表中返回匹配和不匹配的记录。

短内

匹配 + 左不匹配 + 右不匹配 = 完全外连接

匹配 + 左不匹配 = 左外连接

匹配 + 右不匹配 = 右外连接

匹配 = 内连接

Simplest Definitions

Inner Join: Returns matched records from both tables.

Full Outer Join: Returns matched and unmatched records from both tables with null for unmatched records from Both Tables.

Left Outer Join: Returns matched and unmatched records only from table on Left Side.

Right Outer Join: Returns matched and unmatched records only from table on Right Side.

In-Short

Matched + Left Unmatched + Right Unmatched = Full Outer Join

Matched + Left Unmatched = Left Outer Join

Matched + Right Unmatched = Right Outer Join

Matched = Inner Join

黑色毁心梦 2024-07-12 21:46:59

left join on 返回 inner join onunion allnull 扩展的不匹配的左表行。

right join on 返回inner join onunion allnull 扩展的不匹配的右表行。

full join on 返回 inner join on rowsunion allnulls union 扩展的不匹配的左表行由 null 扩展的所有 不匹配的右表行。

outer 是可选的 没有影响。

(SQL Standard 2006 SQL/Foundation 7.7 语法规则 1、一般规则 1 b、3 c 和 d、5 b。)

因此,在了解底层的内部连接之前,请勿外部连接涉及 join on


找出 inner join on 返回哪些行:

这也解释了为什么维恩图对于内部连接和外部连接没有帮助
有关为什么它们通常对连接没有帮助的更多信息:
自然连接的维恩图

left join on returns inner join on rows union all unmatched left table rows extended by nulls.

right join on returns inner join on rows union all unmatched right table rows extended by nulls.

full join on returns inner join on rowsunion all unmatched left table rows extended by nulls union all unmatched right table rows extended by nulls.

outer is optional & has no effect.

(SQL Standard 2006 SQL/Foundation 7.7 Syntax Rules 1, General Rules 1 b, 3 c & d, 5 b.)

So don't outer join on until you know what underlying inner join on is involved.


Find out what rows inner join on returns:
CROSS JOIN vs INNER JOIN in SQL

That also explains why Venn(-like) diagrams are not helpful for inner vs outer join.
For more on why they are not helpful for joins generally:
Venn Diagram for Natural Join

怕倦 2024-07-12 21:46:59

简单来说,

1.INNER JOIN OR EQUI JOIN:返回仅与两个表中的条件匹配的结果集。

2.OUTER JOIN :返回两个表中所有值的结果集,无论条件是否匹配。

3.LEFT JOIN :返回左表中所有值以及右表中符合条件的行的结果集。

4.RIGHT JOIN :返回右表中所有值以及左表中符合条件的行的结果集。

5.FULL JOIN : Full Join 和 Full Outer Join 相同。

In Simple Terms,

1.INNER JOIN OR EQUI JOIN : Returns the resultset that matches only the condition in both the tables.

2.OUTER JOIN : Returns the resultset of all the values from both the tables even if there is condition match or not.

3.LEFT JOIN : Returns the resultset of all the values from left table and only rows that match the condition in right table.

4.RIGHT JOIN : Returns the resultset of all the values from right table and only rows that match the condition in left table.

5.FULL JOIN : Full Join and Full outer Join are same.

孤君无依 2024-07-12 21:46:59

通过示例可以更轻松地解释连接:

在此处输入图像描述

为了模拟存储在单独表中的人员和电子邮件,

表 A 和表 B 由 Table_A 连接起来。id = Table_B.name_id

内连接

在此处输入图像描述

仅显示匹配的 ID 行。

外连接

在此处输入图像描述

显示表 A 的匹配 ID 和不匹配行。

输入图像描述这里

显示表 B 的匹配 ID 和不匹配行。

输入图像描述这里
显示两个表中匹配的 ID 和不匹配的行。

注意:MySQL 上不支持完全外连接

Joins are more easily explained with an example:

enter image description here

To simulate persons and emails stored in separate tables,

Table A and Table B are joined by Table_A.id = Table_B.name_id

Inner Join

enter image description here

Only matched ids' rows are shown.

Outer Joins

enter image description here

Matched ids and not matched rows for Table A are shown.

enter image description here

Matched ids and not matched rows for Table B are shown.

enter image description here
Matched ids and not matched rows from both Tables are shown.

Note: Full outer join is not available on MySQL

坏尐絯℡ 2024-07-12 21:46:59

考虑下面 2 个表:

EMP

empid   name    dept_id salary
1       Rob     1       100
2       Mark    1       300
3       John    2       100
4       Mary    2       300
5       Bill    3       700
6       Jose    6       400

Department

deptid  name
1       IT
2       Accounts
3       Security
4       HR
5       R&D

内连接:

在 sql 查询中大多写成 JOIN 。 它仅返回表之间的匹配记录。

找出所有员工及其部门名称:

Select a.empid, a.name, b.name as dept_name
FROM emp a
JOIN department b
ON a.dept_id = b.deptid
;

empid   name    dept_name
1       Rob     IT
2       Mark    IT
3       John    Accounts
4       Mary    Accounts
5       Bill    Security

如上所示,输出中的 EMP 不会打印 Jose,因为它的 dept_id 6 不会在部门表中查找匹配项。 同样,HRR&D 行不会从 Department 表中打印,因为它们在 Emp 表中找不到匹配项。

因此,INNER JOIN 或只是 JOIN,仅返回匹配的行。

LEFT JOIN:

这将返回 LEFT 表中的所有记录,并且仅返回 RIGHT 表中的匹配记录。

Select a.empid, a.name, b.name as dept_name
FROM emp a
LEFT JOIN department b
ON a.dept_id = b.deptid
;

empid   name    dept_name
1       Rob     IT
2       Mark    IT
3       John    Accounts
4       Mary    Accounts
5       Bill    Security
6       Jose    

因此,如果您观察上面的输出,左表(Emp)中的所有记录都将与右表中的匹配记录一起打印。

HRR&D 行不会从 Department 表中打印,因为它们在 Emp 表中的 dept_id 上找不到匹配项。

因此,LEFT JOIN 返回左表中的所有行,并且仅返回右表中的匹配行。

还可以检查 DEMO 此处

Consider below 2 tables:

EMP

empid   name    dept_id salary
1       Rob     1       100
2       Mark    1       300
3       John    2       100
4       Mary    2       300
5       Bill    3       700
6       Jose    6       400

Department

deptid  name
1       IT
2       Accounts
3       Security
4       HR
5       R&D

Inner Join:

Mostly written as just JOIN in sql queries. It returns only the matching records between the tables.

Find out all employees and their department names:

Select a.empid, a.name, b.name as dept_name
FROM emp a
JOIN department b
ON a.dept_id = b.deptid
;

empid   name    dept_name
1       Rob     IT
2       Mark    IT
3       John    Accounts
4       Mary    Accounts
5       Bill    Security

As you see above, Jose is not printed from EMP in the output as it's dept_id 6 does not find a match in the Department table. Similarly, HR and R&D rows are not printed from Department table as they didn't find a match in the Emp table.

So, INNER JOIN or just JOIN, returns only matching rows.

LEFT JOIN :

This returns all records from the LEFT table and only matching records from the RIGHT table.

Select a.empid, a.name, b.name as dept_name
FROM emp a
LEFT JOIN department b
ON a.dept_id = b.deptid
;

empid   name    dept_name
1       Rob     IT
2       Mark    IT
3       John    Accounts
4       Mary    Accounts
5       Bill    Security
6       Jose    

So, if you observe the above output, all records from the LEFT table(Emp) are printed with just matching records from RIGHT table.

HR and R&D rows are not printed from Department table as they didn't find a match in the Emp table on dept_id.

So, LEFT JOIN returns ALL rows from Left table and only matching rows from RIGHT table.

Can also check DEMO here.

新人笑 2024-07-12 21:46:59

这里有很多很好的答案和非常准确的关系代数示例。 这是一个非常简单的答案,可能对遇到 SQL 编码困境的业余或新手程序员有所帮助。

基本上,JOIN 查询常常归结为两种情况:

对于 A 数据子集的 SELECT

  • 使用 INNER JOIN 当您要查找的相关 B 数据必须存在于每个数据库设计中时;
  • 当您要查找的相关B数据可能可能不存在于每个数据库设计时,请使用LEFT JOIN

There are a lot of good answers here with very accurate relational algebra examples. Here is a very simplified answer that might be helpful for amateur or novice coders with SQL coding dilemmas.

Basically, more often than not, JOIN queries boil down to two cases:

For a SELECT of a subset of A data:

  • use INNER JOIN when the related B data you are looking for MUST exist per database design;
  • use LEFT JOIN when the related B data you are looking for MIGHT or MIGHT NOT exist per database design.
叹梦 2024-07-12 21:46:59
  • 内联接 - 使用任一等效查询的内联接给出两个的交集,即它们具有的两行

  • 左外连接 - 左外连接将给出 A 中的所有行以及 B 中的所有公共行。

  • 完全外连接 - 完全外连接 将为您提供 A 和 B 的并集,即 A 中的所有行和 B 中的所有行。如果 A 中的某些内容在 B 中没有对应的数据,则 B 部分为空,反之亦然反之亦然

  • Inner join - An inner join using either of the equivalent queries gives the intersection of the two tables, i.e. the two rows they have in common.

  • Left outer join - A left outer join will give all rows in A, plus any common rows in B.

  • Full outer join - A full outer join will give you the union of A and B, i.e. All the rows in A and all the rows in B. If something in A doesn't have a corresponding datum in B, then the B portion is null, and vice versay

思慕 2024-07-12 21:46:59

inner joinouter join 之间的区别如下:

  1. Inner join 是根据匹配元组组合表的联接,而 外连接是一种基于匹配和不匹配元组组合表的连接。
  2. 内连接合并两个表中的匹配行,其中省略不匹配的行,而外连接合并两个表中的行,不匹配的行填充空值。
  3. 内连接类似于交集运算,而外连接类似于并集运算。
  4. 内连接有两种类型,而外连接有三种类型。
  5. 外连接内连接更快。

The difference between inner join and outer join is as follow:

  1. Inner join is a join that combined tables based on matching tuples, whereas outer join is a join that combined table based on both matched and unmatched tuple.
  2. Inner join merges matched row from two table in where unmatched row are omitted, whereas outer join merges rows from two tables and unmatched rows fill with null value.
  3. Inner join is like an intersection operation, whereas outer join is like an union operation.
  4. Inner join is two types, whereas outer join are three types.
  5. outer join is faster than inner join.
凯凯我们等你回来 2024-07-12 21:46:59

“外部”和“内部”只是可选元素,您只需处理两(三种)类型的连接。 内部联接(或仅使用“联接”时的默认值)是一种联接,其中仅匹配条件的元素出现在两个表中。

“外部”连接与内部连接相同,加上左表或右表不匹配的元素,在另一个表的所有列上添加空值。

全连接是内部连接加上右连接和左连接。

总之,如果我们有这样的表 A

idA ColumnTableA idB
1 Jonh 1
2 Sarah 1
3 Clark 2
4 Barbie NULL

和表 B 像这样:

idB ColumnTableB
1 Connor
2 Kent
3 Spock

内连接:

from tableA join tableB on tableA.idB = tableB.idB
idA ColumnTableA idB ColumnTableB
1 Jonh 1 Connor
2 Sarah 1 Connor
3 Clark 2 Kent

左外联接:

from tableA left join tableB on tableA.idB = tableB.idB
idA ColumnTableA idB ColumnTableB
1 Jonh 1 Connor
2 Sarah 1 Connor
3 Clark 2 Kent
4 Barbie NULL NULL

右外联接:

from tableA right join tableB on tableA.idB = tableB.idB
idA ColumnTableA idB ColumnTableB
1 Jonh 1 Connor
2 Sarah 1 Connor
3 Clark 2 Kent
NULL NULL 3 Spock

完全外连接:

from tableA full join tableB on tableA.idB = tableB.idB
idA ColumnTableA idB ColumnTableB
1 Jonh 1 Connor
2 Sarah 1 Connor
3 Clark 2 Kent
4 Barbie NULL NULL
NULL NULL 3 Spock

The "outer" and "inner" are just optional elements, you are just dealing with two (three) kinds of joins. Inner joins (or what is the default when using only "join") is a join where only the elements that match the criteria are present on both tables.

The "outer" joins are the same as the inner join plus the elements of the left or right table that didn't match, adding nulls on all columns for the other table.

The full join is the inner plus the right and left joins.

In summary, if we have table A like this

idA ColumnTableA idB
1 Jonh 1
2 Sarah 1
3 Clark 2
4 Barbie NULL

And table B like this:

idB ColumnTableB
1 Connor
2 Kent
3 Spock

The inner join:

from tableA join tableB on tableA.idB = tableB.idB
idA ColumnTableA idB ColumnTableB
1 Jonh 1 Connor
2 Sarah 1 Connor
3 Clark 2 Kent

Left outer join:

from tableA left join tableB on tableA.idB = tableB.idB
idA ColumnTableA idB ColumnTableB
1 Jonh 1 Connor
2 Sarah 1 Connor
3 Clark 2 Kent
4 Barbie NULL NULL

Right outer join:

from tableA right join tableB on tableA.idB = tableB.idB
idA ColumnTableA idB ColumnTableB
1 Jonh 1 Connor
2 Sarah 1 Connor
3 Clark 2 Kent
NULL NULL 3 Spock

Full outer join:

from tableA full join tableB on tableA.idB = tableB.idB
idA ColumnTableA idB ColumnTableB
1 Jonh 1 Connor
2 Sarah 1 Connor
3 Clark 2 Kent
4 Barbie NULL NULL
NULL NULL 3 Spock
笑,眼淚并存 2024-07-12 21:46:59

演示

设置

跳入 psql 并创建一个关于猫和人类的小型数据库。
您只需复制粘贴整个部分即可。

CREATE DATABASE catdb;
\c catdb;
\pset null '[NULL]' -- how to display null values

CREATE TABLE humans (
  name text primary key
);
CREATE TABLE cats (
  human_name text references humans(name),
  name text
);

INSERT INTO humans (name)
VALUES ('Abe'), ('Ann'), ('Ben'), ('Jen');

INSERT INTO cats (human_name, name)
VALUES
('Abe', 'Axel'),
(NULL, 'Bitty'),
('Jen', 'Jellybean'),
('Jen', 'Juniper');

查询

这是一个我们将运行多次的查询,将 [SOMETHING JOIN] 更改为各种类型以查看结果。

SELECT
humans.name AS human_name,
cats.name AS cat_name
FROM humans
[SOMETHING JOIN] cats ON humans.name = cats.human_name
ORDER BY humans.name;

INNER JOIN 返回所有人猫对。
任何没有猫的人或没有人的猫都被排除在外。

 human_name | cat_name
------------+-----------
 Abe        | Axel
 Jen        | Jellybean
 Jen        | Juniper

FULL OUTER JOIN 返回所有人类和所有猫,如果两边都没有匹配项,则返回 NULL

 human_name | cat_name
------------+-----------
 Abe        | Axel
 Ann        | [NULL]
 Ben        | [NULL]
 Jen        | Jellybean
 Jen        | Juniper
 [NULL]     | Bitty

LEFT OUTER JOIN 返回所有人类(左表)。
任何没有猫的人都会在 cat_name 列中得到 NULL
任何没有人类陪伴的猫都被排除在外。

 human_name | cat_name
------------+-----------
 Abe        | Axel
 Ann        | [NULL]
 Ben        | [NULL]
 Jen        | Jellybean
 Jen        | Juniper

RIGHT OUTER JOIN 返回所有猫(右表)。
任何没有人类的猫都会在 human_name 列中得到 NULL
任何没有猫的人都被排除在外。

 human_name | cat_name
------------+-----------
 Abe        | Axel
 Jen        | Jellybean
 Jen        | Juniper
 [NULL]     | Bitty

INNER 与 OUTER

您可以看到,虽然 INNER JOIN 仅获取匹配对,但每种 OUTER 连接都包含一些不匹配的项目。

但是,实际的单词 INNEROUTER 不需要出现在查询中:

  • JOIN 本身就意味着 INNER
  • < code>LEFT JOIN、RIGHT JOINOUTER JOIN 都隐含 OUTER

A Demonstration

Setup

Hop into psql and create a tiny database of cats and humans.
You can just copy-paste this whole section.

CREATE DATABASE catdb;
\c catdb;
\pset null '[NULL]' -- how to display null values

CREATE TABLE humans (
  name text primary key
);
CREATE TABLE cats (
  human_name text references humans(name),
  name text
);

INSERT INTO humans (name)
VALUES ('Abe'), ('Ann'), ('Ben'), ('Jen');

INSERT INTO cats (human_name, name)
VALUES
('Abe', 'Axel'),
(NULL, 'Bitty'),
('Jen', 'Jellybean'),
('Jen', 'Juniper');

Querying

Here's a query we'll run several times, changing [SOMETHING JOIN] to the various types to see the results.

SELECT
humans.name AS human_name,
cats.name AS cat_name
FROM humans
[SOMETHING JOIN] cats ON humans.name = cats.human_name
ORDER BY humans.name;

An INNER JOIN returns all human-cat pairs.
Any human without a cat or cat without a human is excluded.

 human_name | cat_name
------------+-----------
 Abe        | Axel
 Jen        | Jellybean
 Jen        | Juniper

A FULL OUTER JOIN returns all humans and all cats, with NULL if there is no match on either side.

 human_name | cat_name
------------+-----------
 Abe        | Axel
 Ann        | [NULL]
 Ben        | [NULL]
 Jen        | Jellybean
 Jen        | Juniper
 [NULL]     | Bitty

A LEFT OUTER JOIN returns all humans (the left table).
Any human without a cat gets a NULL in the cat_name column.
Any cat without a human is excluded.

 human_name | cat_name
------------+-----------
 Abe        | Axel
 Ann        | [NULL]
 Ben        | [NULL]
 Jen        | Jellybean
 Jen        | Juniper

A RIGHT OUTER JOIN returns all cats (the right table).
Any cat without a human gets a NULL in the human_name column.
Any human without a cat is excluded.

 human_name | cat_name
------------+-----------
 Abe        | Axel
 Jen        | Jellybean
 Jen        | Juniper
 [NULL]     | Bitty

INNER vs OUTER

You can see that while an INNER JOIN gets only matching pairs, each kind of OUTER join includes some items without a match.

However, the actual words INNER and OUTER do not need to appear in queries:

  • JOIN by itself implies INNER
  • LEFT JOIN, RIGHT JOIN and OUTER JOIN all imply OUTER
雾里花 2024-07-12 21:46:59

假设您要连接没有重复的列,这是一种非常常见的情况:

示例

假设您有两个表,每个表有一列,数据如下:

A    B
-    -
1    3
2    4
3    5
4    6

请注意,(1,2) 对于 A 是唯一的,(3,4) 是通用的,(5,6) ) 对于 B 来说是唯一的。

内联接

使用任一等效查询的内联接给出两个表的交集,即它们共有的两行。

select * from a INNER JOIN b on a.a = b.b;
select a.*, b.*  from a,b where a.a = b.b;

a | b
--+--
3 | 3
4 | 4

左外连接

左外连接将给出 A 中的所有行,以及 B 中的所有公共行。

select * from a LEFT OUTER JOIN b on a.a = b.b;
select a.*, b.*  from a,b where a.a = b.b(+);

a |  b
--+-----
1 | null
2 | null
3 |    3
4 |    4

右外连接

右外连接将给出 B 中的所有行,再加上A 中的任何公共行。

select * from a RIGHT OUTER JOIN b on a.a = b.b;
select a.*, b.*  from a,b where a.a(+) = b.b;

a    |  b
-----+----
3    |  3
4    |  4
null |  5
null |  6

完全外连接

完全外连接将为您提供 A 和 B 的并集,即 A 中的所有行和 B 中的所有行。如果 A 中的某些内容不存在B 中有相应的数据,则 B 部分为空,反之亦然。

select * from a FULL OUTER JOIN b on a.a = b.b;

 a   |  b
-----+-----
   1 | null
   2 | null
   3 |    3
   4 |    4
null |    6
null |    5

Assuming you're joining on columns with no duplicates, which is a very common case:

  • An inner join of A and B gives the result of A intersect B, i.e. the inner part of a Venn diagram intersection.

  • An outer join of A and B gives the results of A union B, i.e. the outer parts of a Venn diagram union.

Examples

Suppose you have two tables, with a single column each, and data as follows:

A    B
-    -
1    3
2    4
3    5
4    6

Note that (1,2) are unique to A, (3,4) are common, and (5,6) are unique to B.

Inner join

An inner join using either of the equivalent queries gives the intersection of the two tables, i.e. the two rows they have in common.

select * from a INNER JOIN b on a.a = b.b;
select a.*, b.*  from a,b where a.a = b.b;

a | b
--+--
3 | 3
4 | 4

Left outer join

A left outer join will give all rows in A, plus any common rows in B.

select * from a LEFT OUTER JOIN b on a.a = b.b;
select a.*, b.*  from a,b where a.a = b.b(+);

a |  b
--+-----
1 | null
2 | null
3 |    3
4 |    4

Right outer join

A right outer join will give all rows in B, plus any common rows in A.

select * from a RIGHT OUTER JOIN b on a.a = b.b;
select a.*, b.*  from a,b where a.a(+) = b.b;

a    |  b
-----+----
3    |  3
4    |  4
null |  5
null |  6

Full outer join

A full outer join will give you the union of A and B, i.e. all the rows in A and all the rows in B. If something in A doesn't have a corresponding datum in B, then the B portion is null, and vice versa.

select * from a FULL OUTER JOIN b on a.a = b.b;

 a   |  b
-----+-----
   1 | null
   2 | null
   3 |    3
   4 |    4
null |    6
null |    5
笨笨の傻瓜 2024-07-12 21:46:59

您还可以考虑针对不同连接类型的以下架构;

连接的视觉解释

来源:Visual-Representation-of-SQL-JoinsCL 莫法特

Also you can consider the following schema for different join types;

visual explanation of joins

Source: Visual-Representation-of-SQL-Joins explained in detail by C.L. Moffatt

錯遇了你 2024-07-12 21:46:59

维恩图并不真正适合我。

例如,它们不显示交叉连接和内部连接之间的任何区别,或者更一般地显示不同类型的连接谓词之间的任何区别,或提供用于推理它们如何操作的框架。

理解逻辑处理是无可替代的,而且无论如何掌握起来相对简单。

  1. 想象一下交叉连接。
  2. 针对步骤 1 中的所有行评估 on 子句,保留谓词计算结果为 true 的行
  3. (仅适用于外部联接)添加回步骤 2 中丢失的任何外部行 。

(注意:实际上,查询优化器可能会找到比上面的纯逻辑描述更有效的执行查询的方法,但最终结果必须相同)

我将从全外连接的动画版本开始 。 进一步的解释如下。

输入图像描述这里


说明

源表

在此处输入链接描述

首先以 <代码>交叉连接(又名笛卡尔积)。 它没有 ON 子句,只是返回两个表中行的每个组合。

从 A CROSS JOIN B 中选择 A.Colour、B.Colour

在此处输入链接描述

内部和外连接有一个“ON”子句谓词。

  • 内部联接。评估交叉联接结果中所有行的“ON”子句中的条件。 如果 true 返回连接的行。 否则丢弃它。
  • 左外连接。与内连接相同,然后对于左表中与任何内容不匹配的任何行,输出这些行并为右表列输出 NULL 值。
  • 右外连接。与内连接相同,然后对于右表中与任何内容不匹配的任何行,输出这些行并为左表列输出 NULL 值。
  • 完全外连接。与内连接相同,然后保留左外连接中的左非匹配行,并根据右外连接保留右非匹配行。

一些示例

SELECT A.Colour, B.Colour FROM A INNER JOIN B ON A.Colour = B.Colour

以上是经典的等值连接。

Inner Join

动画版

在此处输入图像描述

从 A 上的内部连接 ​​B 中选择 A.Colour、B.Colour .Colour NOT IN ('Green','Blue')

内连接条件不一定是相等条件,并且它不需要引用两个表(甚至任一表)中的列。 在交叉连接的每一行上计算 A.Colour NOT IN ('Green','Blue') 返回。

inner 2

从 1 上的内部连接 ​​B 选择 A.Colour、B.Colour =1

对于交叉连接结果中的所有行,连接条件的计算结果为 true,因此这与交叉连接相同。 16行的图我就不再重复了。

SELECT A.Colour, B.Colour FROM A LEFT OUTER JOIN B ON A.Colour = B.Colour

外部联接的逻辑计算方式与内部联接相同,除非左表中的一行(对于左联接)根本不与右侧表中的任何行连接,它会保留在结果中,右侧列的值为 NULL。

LOJ

从 A.Colour = B.Colour 的左外连接 B 中选择 A.Colour、B.Colour,其中 B. Color IS NULL

这只是将先前的结果限制为仅返回 B.Colour IS NULL 的行。 在这种特殊情况下,这些行将被保留,因为它们在右侧表中没有匹配项,并且查询返回在表 B 中不匹配的单个红色行。 这称为反半连接。

IS NULL 测试选择一个列非常重要,该列要么不可为空,要么其连接条件确保任何 NULL 值都将被排除在该模式中正确工作并避免只返回除了不匹配的行之外该列恰好具有 NULL 值的行。

loj is null

上的右外连接 B 中选择 A.Colour、B.Colour

从 A.Colour = B.Colour Right 外连接的作用与左外连接类似,只是它们保留右表中的非匹配行,并且用 null 扩展左手列。

ROJ

从 A.Colour = B.Colour 的完整外部联接 B 中选择 A.Colour、B.Colour

完整外部联接结合左连接和右连接的行为并保留左表和右表中的不匹配行。

FOJ

SELECT A.Colour, B.Colour FROM A FULL OUTER JOIN B ON 1 = 0

交叉连接匹配中没有行1=0 谓词。 使用正常的外连接规则保留两侧的所有行,并在另一侧的表的列中使用 NULL。

FOJ 2

完整外部连接 B ON 1 = 0 中选择 COALESCE(A.Colour, B.Colour) 作为颜色

从 对前面的查询稍加修改就可以模拟两个表的UNION ALL

UNION ALL

从 A.Colour = B.Colour WHERE B 的左外连接 B 中选择 A.Colour、B.Colour .Colour = 'Green'

请注意,WHERE 子句(如果存在)逻辑上在连接之后运行。 一个常见的错误是执行左外连接,然后在右表中包含一个带有条件的 WHERE 子句,最终排除不匹配的行。 上面最终执行了外连接...

LOJ

...然后运行“Where”子句。 NULL= 'Green' 计算结果不为 true,因此外部联接保留的行最终会被丢弃(连同蓝色行),从而有效地将联接转换回内部联接。

LOJtoInner

如果目的是仅包含 B 中颜色为绿色的行以及 A 中的所有行(无论语法是否正确)

从 A.Colour = B.Colour AND B.Colour = 'Green' 上的左外连接 B 选择 A.Colour、B.Colour

在此处输入图像描述

SQL Fiddle

查看这些示例在 SQLFiddle.com 上实时运行

The Venn diagrams don't really do it for me.

They don't show any distinction between a cross join and an inner join, for example, or more generally show any distinction between different types of join predicate or provide a framework for reasoning about how they will operate.

There is no substitute for understanding the logical processing and it is relatively straightforward to grasp anyway.

  1. Imagine a cross join.
  2. Evaluate the on clause against all rows from step 1 keeping those where the predicate evaluates to true
  3. (For outer joins only) add back in any outer rows that were lost in step 2.

(NB: In practice the query optimiser may find more efficient ways of executing the query than the purely logical description above but the final result must be the same)

I'll start off with an animated version of a full outer join. Further explanation follows.

enter image description here


Explanation

Source Tables

enter link description here

First start with a CROSS JOIN (AKA Cartesian Product). This does not have an ON clause and simply returns every combination of rows from the two tables.

SELECT A.Colour, B.Colour FROM A CROSS JOIN B

enter link description here

Inner and Outer joins have an "ON" clause predicate.

  • Inner Join. Evaluate the condition in the "ON" clause for all rows in the cross join result. If true return the joined row. Otherwise discard it.
  • Left Outer Join. Same as inner join then for any rows in the left table that did not match anything output these with NULL values for the right table columns.
  • Right Outer Join. Same as inner join then for any rows in the right table that did not match anything output these with NULL values for the left table columns.
  • Full Outer Join. Same as inner join then preserve left non matched rows as in left outer join and right non matching rows as per right outer join.

Some examples

SELECT A.Colour, B.Colour FROM A INNER JOIN B ON A.Colour = B.Colour

The above is the classic equi join.

Inner Join

Animated Version

enter image description here

SELECT A.Colour, B.Colour FROM A INNER JOIN B ON A.Colour NOT IN ('Green','Blue')

The inner join condition need not necessarily be an equality condition and it need not reference columns from both (or even either) of the tables. Evaluating A.Colour NOT IN ('Green','Blue') on each row of the cross join returns.

inner 2

SELECT A.Colour, B.Colour FROM A INNER JOIN B ON 1 =1

The join condition evaluates to true for all rows in the cross join result so this is just the same as a cross join. I won't repeat the picture of the 16 rows again.

SELECT A.Colour, B.Colour FROM A LEFT OUTER JOIN B ON A.Colour = B.Colour

Outer Joins are logically evaluated in the same way as inner joins except that if a row from the left table (for a left join) does not join with any rows from the right hand table at all it is preserved in the result with NULL values for the right hand columns.

LOJ

SELECT A.Colour, B.Colour FROM A LEFT OUTER JOIN B ON A.Colour = B.Colour WHERE B.Colour IS NULL

This simply restricts the previous result to only return the rows where B.Colour IS NULL. In this particular case these will be the rows that were preserved as they had no match in the right hand table and the query returns the single red row not matched in table B. This is known as an anti semi join.

It is important to select a column for the IS NULL test that is either not nullable or for which the join condition ensures that any NULL values will be excluded in order for this pattern to work correctly and avoid just bringing back rows which happen to have a NULL value for that column in addition to the un matched rows.

loj is null

SELECT A.Colour, B.Colour FROM A RIGHT OUTER JOIN B ON A.Colour = B.Colour

Right outer joins act similarly to left outer joins except they preserve non matching rows from the right table and null extend the left hand columns.

ROJ

SELECT A.Colour, B.Colour FROM A FULL OUTER JOIN B ON A.Colour = B.Colour

Full outer joins combine the behaviour of left and right joins and preserve the non matching rows from both the left and the right tables.

FOJ

SELECT A.Colour, B.Colour FROM A FULL OUTER JOIN B ON 1 = 0

No rows in the cross join match the 1=0 predicate. All rows from both sides are preserved using normal outer join rules with NULL in the columns from the table on the other side.

FOJ 2

SELECT COALESCE(A.Colour, B.Colour) AS Colour FROM A FULL OUTER JOIN B ON 1 = 0

With a minor amend to the preceding query one could simulate a UNION ALL of the two tables.

UNION ALL

SELECT A.Colour, B.Colour FROM A LEFT OUTER JOIN B ON A.Colour = B.Colour WHERE B.Colour = 'Green'

Note that the WHERE clause (if present) logically runs after the join. One common error is to perform a left outer join and then include a WHERE clause with a condition on the right table that ends up excluding the non matching rows. The above ends up performing the outer join...

LOJ

... And then the "Where" clause runs. NULL= 'Green' does not evaluate to true so the row preserved by the outer join ends up discarded (along with the blue one) effectively converting the join back to an inner one.

LOJtoInner

If the intention was to include only rows from B where Colour is Green and all rows from A regardless the correct syntax would be

SELECT A.Colour, B.Colour FROM A LEFT OUTER JOIN B ON A.Colour = B.Colour AND B.Colour = 'Green'

enter image description here

SQL Fiddle

See these examples run live at SQLFiddle.com.

放我走吧 2024-07-12 21:46:59

简而言之:

内部联接仅检索匹配的行。

外连接从一个表中检索匹配的行以及另一表中的所有行......结果取决于您使用的是哪一个:

  • Left:右表中的匹配行和左表中的所有行

  • Right : 左表中的匹配行和右表中的所有行或

  • Full< /strong>:所有表中的所有行。 匹配与否并不重要

In simple words:

An inner join retrieve the matched rows only.

Whereas an outer join retrieve the matched rows from one table and all rows in other table ....the result depends on which one you are using:

  • Left: Matched rows in the right table and all rows in the left table

  • Right: Matched rows in the left table and all rows in the right table or

  • Full: All rows in all tables. It doesn't matter if there is a match or not

甜警司 2024-07-12 21:46:59

如果连接的另一(右侧)侧有匹配的记录,则内部连接仅显示行。

(左)外部联接在左侧显示每条记录的行,即使联接的另一(右侧)侧没有匹配的行。 如果没有匹配的行,另一侧(右侧)的列将显示 NULL。

A inner join only shows rows if there is a matching record on the other (right) side of the join.

A (left) outer join shows rows for each record on the left hand side, even if there are no matching rows on the other (right) side of the join. If there is no matching row, the columns for the other (right) side would show NULLs.

蓝海 2024-07-12 21:46:59

内联接要求联接表中存在具有相关 ID 的记录。

即使右侧不存在任何记录,外连接也会返回左侧的记录。

例如,您有一个 Orders 和一个 OrderDetails 表。 它们通过“OrderID”相关。

Orders

  • OrderID
  • CustomerName

OrderDetails

  • OrderDetailID
  • OrderID
  • ProductName
  • Qty
  • Price

该请求

SELECT Orders.OrderID, Orders.CustomerName
  FROM Orders 
 INNER JOIN OrderDetails
    ON Orders.OrderID = OrderDetails.OrderID

将仅返回在 OrderDetails 表中也有某些内容的订单。

如果将其更改为 OUTER LEFT JOIN,

SELECT Orders.OrderID, Orders.CustomerName
  FROM Orders 
  LEFT JOIN OrderDetails
    ON Orders.OrderID = OrderDetails.OrderID

那么它将返回 Orders 表中的记录,即使它们没有 OrderDetails 记录。

您可以通过添加像 WHERE OrderDetails.OrderID IS NULL 这样的 where 子句来使用它来查找没有任何 OrderDetails 指示可能孤立订单的订单。

Inner joins require that a record with a related ID exist in the joined table.

Outer joins will return records for the left side even if nothing exists for the right side.

For instance, you have an Orders and an OrderDetails table. They are related by an "OrderID".

Orders

  • OrderID
  • CustomerName

OrderDetails

  • OrderDetailID
  • OrderID
  • ProductName
  • Qty
  • Price

The request

SELECT Orders.OrderID, Orders.CustomerName
  FROM Orders 
 INNER JOIN OrderDetails
    ON Orders.OrderID = OrderDetails.OrderID

will only return Orders that also have something in the OrderDetails table.

If you change it to OUTER LEFT JOIN

SELECT Orders.OrderID, Orders.CustomerName
  FROM Orders 
  LEFT JOIN OrderDetails
    ON Orders.OrderID = OrderDetails.OrderID

then it will return records from the Orders table even if they have no OrderDetails records.

You can use this to find Orders that do not have any OrderDetails indicating a possible orphaned order by adding a where clause like WHERE OrderDetails.OrderID IS NULL.

情话墙 2024-07-12 21:46:59

简而言之:

内连接 -> 仅从父表和子表中获取公共记录,其中父表的主键与子表中的外键匹配。

左连接 ->

伪代码

1.Take All records from left Table
2.for(each record in right table,) {
    if(Records from left & right table matching on primary & foreign key){
       use their values as it is as result of join at the right side for 2nd table.
    } else {
       put value NULL values in that particular record as result of join at the right side for 2nd table.
    }
  }

Right join :与 left join 完全相反。 将 LEFT JOIN 中的表名称放在 Right join 的右侧,您将获得与 LEFT JOIN 相同的输出。

外连接:显示两个表中的所有记录无论如何。 如果左表中的记录与基于主键、外键的右表不匹配,则使用 NULL 值作为连接结果。

示例:

Example

现在假设有 2 个表

1.employees 、 2.phone_numbers_employees

employees : id , name 

phone_numbers_employees : id , phone_num , emp_id   

这里,employees 表是主表,phone_numbers_employees 是子表(它包含 emp_id 作为外键,连接 employee.id 所以它的子表。)

内连接

获取2个表的记录仅当员工表的主键(其id)与子表phone_numbers_employees(emp_id)的外键匹配时

因此,查询将是:

SELECT e.id , e.name , p.phone_num FROM employees AS e INNER JOIN phone_numbers_employees AS p ON e.id = p.emp_id;

这里仅采用主键 = 外键上的匹配行,如上所述。这里,作为连接结果,主键 = 外键上的非匹配行将被跳过。

左连接

左连接保留左表的所有行,无论右表是否有匹配的行。

SELECT e.id , e.name , p.phone_num FROM employees AS e LEFT JOIN phone_numbers_employees AS p ON e.id = p.emp_id;

外连接

SELECT e.id , e.name , p.phone_num FROM employees AS e OUTER JOIN phone_numbers_employees AS p ON e.id = p.emp_id;

从图表上看,它看起来像:

Diagram

In simple words :

Inner join -> Take ONLY common records from parent and child tables WHERE primary key of Parent table matches Foreign key in Child table.

Left join ->

pseudo code

1.Take All records from left Table
2.for(each record in right table,) {
    if(Records from left & right table matching on primary & foreign key){
       use their values as it is as result of join at the right side for 2nd table.
    } else {
       put value NULL values in that particular record as result of join at the right side for 2nd table.
    }
  }

Right join : Exactly opposite of left join . Put name of table in LEFT JOIN at right side in Right join , you get same output as LEFT JOIN.

Outer join : Show all records in Both tables No matter what. If records in Left table are not matching to right table based on Primary , Forieign key , use NULL value as result of join .

Example :

Example

Lets assume now for 2 tables

1.employees , 2.phone_numbers_employees

employees : id , name 

phone_numbers_employees : id , phone_num , emp_id   

Here , employees table is Master table , phone_numbers_employees is child table(it contains emp_id as foreign key which connects employee.id so its child table.)

Inner joins

Take the records of 2 tables ONLY IF Primary key of employees table(its id) matches Foreign key of Child table phone_numbers_employees(emp_id).

So query would be :

SELECT e.id , e.name , p.phone_num FROM employees AS e INNER JOIN phone_numbers_employees AS p ON e.id = p.emp_id;

Here take only matching rows on primary key = foreign key as explained above.Here non matching rows on primary key = foreign key are skipped as result of join.

Left joins :

Left join retains all rows of the left table, regardless of whether there is a row that matches on the right table.

SELECT e.id , e.name , p.phone_num FROM employees AS e LEFT JOIN phone_numbers_employees AS p ON e.id = p.emp_id;

Outer joins :

SELECT e.id , e.name , p.phone_num FROM employees AS e OUTER JOIN phone_numbers_employees AS p ON e.id = p.emp_id;

Diagramatically it looks like :

Diagram

三寸金莲 2024-07-12 21:46:59

INNER JOIN 要求在比较两个表时至少存在匹配项。 例如,表A和表B意味着A ٨ B(A交集B)。

LEFT OUTER JOINLEFT JOIN 是相同的。 它给出了两个表中匹配的所有记录以及左表的所有可能性。

同样,RIGHT OUTER JOIN 和 RIGHT JOIN 是相同的。 它给出了两个表中匹配的所有记录以及右表的所有可能性。

FULL JOINLEFT OUTER JOINRIGHT OUTER JOIN 的组合,没有重复。

INNER JOIN requires there is at least a match in comparing the two tables. For example, table A and table B which implies A ٨ B (A intersection B).

LEFT OUTER JOIN and LEFT JOIN are the same. It gives all the records matching in both tables and all possibilities of the left table.

Similarly, RIGHT OUTER JOIN and RIGHT JOIN are the same. It gives all the records matching in both tables and all possibilities of the right table.

FULL JOIN is the combination of LEFT OUTER JOIN and RIGHT OUTER JOIN without duplication.

泛泛之交 2024-07-12 21:46:59

答案在于每一个的含义,因此也在于结果。

注意:
SQLite 中,没有 RIGHT OUTER JOINFULL OUTER JOIN
而且在 MySQL 中也没有 FULL OUTER JOIN

我的回答是基于上面的注释

当您有两个像这样的表时:

--[table1]               --[table2]
id | name                id | name
---+-------              ---+-------
1  | a1                  1  | a2
2  | b1                  3  | b2

CROSS JOIN / OUTER JOIN:
您可以使用 CROSS JOIN 或仅使用 , 获取所有这些表数据,如下所示:

SELECT * FROM table1, table2
--[OR]
SELECT * FROM table1 CROSS JOIN table2

--[Results:]
id | name | id | name 
---+------+----+------
1  | a1   | 1  | a2
1  | a1   | 3  | b2
2  | b1   | 1  | a2
2  | b1   | 3  | b2

INNER JOIN :
当您想基于 table1.id = table2.id 等关系向上述结果添加过滤器时,可以使用 INNER JOIN

SELECT * FROM table1, table2 WHERE table1.id = table2.id
--[OR]
SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id

--[Results:]
id | name | id | name 
---+------+----+------
1  | a1   | 1  | a2

LEFT [OUTER] JOIN :
当您想要在上述结果中获得其中一个表的所有行(具有相同的关系)时,您可以使用LEFT JOIN
(对于RIGHT JOIN,只需更改表的位置)

SELECT * FROM table1, table2 WHERE table1.id = table2.id 
UNION ALL
SELECT *, Null, Null FROM table1 WHERE Not table1.id In (SELECT id FROM table2)
--[OR]
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id

--[Results:]
id | name | id   | name 
---+------+------+------
1  | a1   | 1    | a2
2  | b1   | Null | Null

FULL OUTER JOIN:
当您还想在结果中包含另一个表的所有行时,您可以使用FULL OUTER JOIN

SELECT * FROM table1, table2 WHERE table1.id = table2.id
UNION ALL
SELECT *, Null, Null FROM table1 WHERE Not table1.id In (SELECT id FROM table2)
UNION ALL
SELECT Null, Null, * FROM table2 WHERE Not table2.id In (SELECT id FROM table1)
--[OR] (recommended for SQLite)
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id
UNION ALL
SELECT * FROM table2 LEFT JOIN table1 ON table2.id = table1.id
WHERE table1.id IS NULL
--[OR]
SELECT * FROM table1 FULL OUTER JOIN table2 On table1.id = table2.id

--[Results:]
id   | name | id   | name 
-----+------+------+------
1    | a1   | 1    | a2
2    | b1   | Null | Null
Null | Null | 3    | b2

好吧,根据您的需要,您可以选择满足您需要的每一个;)。

The answer is in the meaning of each one, so in the results.

Note :
In SQLite there is no RIGHT OUTER JOIN or FULL OUTER JOIN.
And also in MySQL there is no FULL OUTER JOIN.

My answer is based on above Note.

When you have two tables like these:

--[table1]               --[table2]
id | name                id | name
---+-------              ---+-------
1  | a1                  1  | a2
2  | b1                  3  | b2

CROSS JOIN / OUTER JOIN :
You can have all of those tables data with CROSS JOIN or just with , like this:

SELECT * FROM table1, table2
--[OR]
SELECT * FROM table1 CROSS JOIN table2

--[Results:]
id | name | id | name 
---+------+----+------
1  | a1   | 1  | a2
1  | a1   | 3  | b2
2  | b1   | 1  | a2
2  | b1   | 3  | b2

INNER JOIN :
When you want to add a filter to above results based on a relation like table1.id = table2.id you can use INNER JOIN:

SELECT * FROM table1, table2 WHERE table1.id = table2.id
--[OR]
SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id

--[Results:]
id | name | id | name 
---+------+----+------
1  | a1   | 1  | a2

LEFT [OUTER] JOIN :
When you want to have all rows of one of tables in the above result -with same relation- you can use LEFT JOIN:
(For RIGHT JOIN just change place of tables)

SELECT * FROM table1, table2 WHERE table1.id = table2.id 
UNION ALL
SELECT *, Null, Null FROM table1 WHERE Not table1.id In (SELECT id FROM table2)
--[OR]
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id

--[Results:]
id | name | id   | name 
---+------+------+------
1  | a1   | 1    | a2
2  | b1   | Null | Null

FULL OUTER JOIN :
When you also want to have all rows of the other table in your results you can use FULL OUTER JOIN:

SELECT * FROM table1, table2 WHERE table1.id = table2.id
UNION ALL
SELECT *, Null, Null FROM table1 WHERE Not table1.id In (SELECT id FROM table2)
UNION ALL
SELECT Null, Null, * FROM table2 WHERE Not table2.id In (SELECT id FROM table1)
--[OR] (recommended for SQLite)
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id
UNION ALL
SELECT * FROM table2 LEFT JOIN table1 ON table2.id = table1.id
WHERE table1.id IS NULL
--[OR]
SELECT * FROM table1 FULL OUTER JOIN table2 On table1.id = table2.id

--[Results:]
id   | name | id   | name 
-----+------+------+------
1    | a1   | 1    | a2
2    | b1   | Null | Null
Null | Null | 3    | b2

Well, as your need you choose each one that covers your need ;).

草莓酥 2024-07-12 21:46:59

内部联接。

联接是将两个表中的行组合在一起。 内连接尝试根据您在查询中指定的条件来匹配两个表,并且仅返回匹配的行。 如果连接中第一个表中的一行与第二个表中的两行匹配,则结果中将返回两行。 如果第一个表中的行与第二个表中的行不匹配,则不会返回; 同样,如果第二个表中的行与第一个表中的行不匹配,则不会返回该行。

外连接。

左连接尝试查找第一个表中的行与第二个表中的行的匹配。 如果找不到匹配项,它将返回第一个表中的列,并将第二个表中的列留空 (null)。

Inner join.

A join is combining the rows from two tables. An inner join attempts to match up the two tables based on the criteria you specify in the query, and only returns the rows that match. If a row from the first table in the join matches two rows in the second table, then two rows will be returned in the results. If there’s a row in the first table that doesn’t match a row in the second, it’s not returned; likewise, if there’s a row in the second table that doesn’t match a row in the first, it’s not returned.

Outer Join.

A left join attempts to find match up the rows from the first table to rows in the second table. If it can’t find a match, it will return the columns from the first table and leave the columns from the second table blank (null).

后eg是否自 2024-07-12 21:46:59

输入图像描述这里

  • INNER JOIN 最典型的两个或多个表的连接。
    它返回表 ON 主键和外键关系上的数据匹配。
  • OUTER JOININNER JOIN 相同,但它还包含 ResultSet 上的 NULL 数据。
    • LEFT JOIN = INNER JOIN + 表中的数据不匹配,而右表中的Null匹配。< /里>
    • RIGHT JOIN = INNER JOIN + 表中的数据不匹配,而左表中的Null匹配。< /里>
    • FULL JOIN = INNER JOIN + 左右表上的数据不匹配且 Null 匹配。< /里>
  • 自连接不是 SQL 中的关键字,当表本身引用数据时称为自连接。 使用INNER JOINOUTER JOIN我们可以编写自连接查询。

例如:

SELECT * 
FROM   tablea a 
       INNER JOIN tableb b 
               ON a.primary_key = b.foreign_key 
       INNER JOIN tablec c 
               ON b.primary_key = c.foreign_key 

enter image description here

  • INNER JOIN most typical join for two or more tables.
    It returns data match on both table ON primarykey and forignkey relation.
  • OUTER JOIN is same as INNER JOIN, but it also include NULL data on ResultSet.
    • LEFT JOIN = INNER JOIN + Unmatched data of left table with Null match on right table.
    • RIGHT JOIN = INNER JOIN + Unmatched data of right table with Null match on left table.
    • FULL JOIN = INNER JOIN + Unmatched data on both right and left tables with Null matches.
  • Self join is not a keyword in SQL, when a table references data in itself knows as self join. Using INNER JOIN and OUTER JOIN we can write self join queries.

For example:

SELECT * 
FROM   tablea a 
       INNER JOIN tableb b 
               ON a.primary_key = b.foreign_key 
       INNER JOIN tablec c 
               ON b.primary_key = c.foreign_key 
弥繁 2024-07-12 21:46:59

在批评了备受喜爱的红色阴影维恩图之后,我认为发布我自己的尝试才是公平的。

尽管@Martin Smith的答案是这组答案中最好的,但他只显示每个表中的关键列,而我认为理想情况下也应该显示非关键列。

我在允许的半小时内所能做的最好的事情,我仍然不认为它充分表明由于 TableB 中缺少键值或 OUTER JOIN 中存在空值> 实际上是联合而不是联接:

在此处输入图像描述

Having criticized the much-loved red-shaded Venn diagram, I thought it only fair to post my own attempt.

Although @Martin Smith's answer is the best of this bunch by a long way, his only shows the key column from each table, whereas I think ideally non-key columns should also be shown.

The best I could do in the half hour allowed, I still don't think it adequately shows that the nulls are there due to absence of key values in TableB or that OUTER JOIN is actually a union rather than a join:

enter image description here

朕就是辣么酷 2024-07-12 21:46:59

我在其他答案中没有看到有关性能和优化器的太多细节。

有时,最好知道只有 INNER JOIN 是关联的,这意味着优化器有最多的选择来使用它。 它可以重新排序连接顺序,使其更快地保持相同的结果。 优化器可以使用最多的连接模式。

一般来说,尝试使用 INNER JOIN 而不是不同类型的联接是一个很好的做法。 (当然,如果可以考虑预期的结果集。)

这里有一些关于这种奇怪的关联行为的很好的例子和解释:

I don't see much details about performance and optimizer in the other answers.

Sometimes it is good to know that only INNER JOIN is associative which means the optimizer has the most option to play with it. It can reorder the join order to make it faster keeping the same result. The optimizer can use the most join modes.

Generally it is a good practice to try to use INNER JOIN instead of the different kind of joins. (Of course if it is possible considering the expected result set.)

There are a couple of good examples and explanation here about this strange associative behavior:

假扮的天使 2024-07-12 21:46:59

INNER JOINLEFT/RIGHT OUTER JOIN 的精确算法如下:

  1. 从第一个表中取出每一行:a
  2. 考虑来自旁边的第二个表:(a, b[i])
  3. 针对每对评估 ON ... 子句:ON( a, b[i] ) =真/假?
    • 当条件计算结果为true时,返回组合行(a, b[i])
    • 当到达第二个表的末尾且没有任何匹配项时,这是一个外部联接,则使用 Null 返回一个(虚拟) 对其他表的所有列:(a, Null) 对于左外连接,或 (Null, b) 对于右外连接。 这是为了确保最终结果中存在第一个表的所有行。

注意:ON子句中指定的条件可以是任何内容,不需要使用主键(并且您不需要总是请参阅两个表中的列)! 例如:

  • ... ON T1.title = T2.title AND T1.version < T2.version ( => 请参阅这篇文章作为示例用法:仅选择列上具有最大值的行)
  • ... ON T1.y IS NULL
  • ... ON 1 = 0 (正如示例)

内连接与左外连接


在此处输入图像描述

注意: 左连接 = 左外连接,右连接 = 右外连接。

The precise algorithm for INNER JOIN, LEFT/RIGHT OUTER JOIN are as following:

  1. Take each row from the first table: a
  2. Consider all rows from second table beside it: (a, b[i])
  3. Evaluate the ON ... clause against each pair: ON( a, b[i] ) = true/false?
    • When the condition evaluates to true, return that combined row (a, b[i]).
    • When reach end of second table without any match, and this is an Outer Join then return a (virtual) pair using Null for all columns of other table: (a, Null) for LEFT outer join or (Null, b) for RIGHT outer join. This is to ensure all rows of first table exists in final results.

Note: the condition specified in ON clause could be anything, it is not required to use Primary Keys (and you don't need to always refer to Columns from both tables)! For example:

Inner Join vs. Left Outer Join


enter image description here

Note: Left Join = Left Outer Join, Right Join = Right Outer Join.

伴随着你 2024-07-12 21:46:59

总体思路

请参阅 答案,作者: Martin Smith 对不同连接有更好的说明和解释,包括特别是 FULL OUTER JOIN 、RIGHT OUTER JOINLEFT OUTER JOIN 之间的区别

这两个表构成了以下 JOIN 表示的基础:

Basis

CROSS JOIN

< img src="https://i.sstatic.net/EqnB9.png" alt="CrossJoin">

SELECT *
  FROM citizen
 CROSS JOIN postalcode

结果将是所有组合的笛卡尔积。 不需要 JOIN 条件:

CrossJoinResult

INNER JOIN

INNER JOIN 是与简单相同: JOIN

InnerJoin

SELECT *
  FROM citizen    c
  JOIN postalcode p ON c.postal = p.postal

结果将是满足所需 JOIN 的组合 条件:

InnerJoinResult

LEFT OUTER JOIN

LEFT OUTER JOINLEFT JOIN

LeftJoin

SELECT *
  FROM citizen         c
  LEFT JOIN postalcode p ON c.postal = p.postal

结果将是来自 citizen 的所有内容,即使有邮政编码中没有匹配项。 再次需要 JOIN 条件:

LeftJoinResult

播放数据

所有示例均已在 Oracle 上运行18c。 它们可以在 dbfiddle.uk 上找到,这也是表格屏幕截图的来源。

CREATE TABLE citizen (id      NUMBER,
                      name    VARCHAR2(20),
                      postal  NUMBER,  -- <-- could do with a redesign to postalcode.id instead.
                      leader  NUMBER);

CREATE TABLE postalcode (id      NUMBER,
                         postal  NUMBER,
                         city    VARCHAR2(20),
                         area    VARCHAR2(20));

INSERT INTO citizen (id, name, postal, leader)
              SELECT 1, 'Smith', 2200,  null FROM DUAL
        UNION SELECT 2, 'Green', 31006, 1    FROM DUAL
        UNION SELECT 3, 'Jensen', 623,  1    FROM DUAL;

INSERT INTO postalcode (id, postal, city, area)
                 SELECT 1, 2200,     'BigCity',         'Geancy'  FROM DUAL
           UNION SELECT 2, 31006,    'SmallTown',       'Snizkim' FROM DUAL
           UNION SELECT 3, 31006,    'Settlement',      'Moon'    FROM DUAL  -- <-- Uuh-uhh.
           UNION SELECT 4, 78567390, 'LookoutTowerX89', 'Space'   FROM DUAL;

使用 JOINWHERE

CROSS JOIN

CROSS JOIN 时边界模糊,导致行作为总体思路/INNER JOIN

SELECT *
  FROM citizen          c
  CROSS JOIN postalcode p
 WHERE c.postal = p.postal -- < -- The WHERE condition is limiting the resulting rows

使用CROSS JOIN来获取LEFT OUTER JOIN的结果需要一些技巧,例如添加NULL行。 它被省略了。

INNER JOIN

INNER JOIN 成为笛卡尔积。 它与总体思路/CROSS JOIN 相同:

SELECT *
  FROM citizen    c
  JOIN postalcode p ON 1 = 1  -- < -- The ON condition makes it a CROSS JOIN

这就是内部联接真正可以被视为交叉联接,结果与条件不匹配的情况被删除。 这里没有删除任何结果行。

使用 INNER JOIN 来获取 LEFT OUTER JOIN 的结果也需要一些技巧。 它被省略了。

LEFT OUTER JOIN

LEFT JOIN 产生的行为 The General Idea/CROSS JOIN:

SELECT *
  FROM citizen         c
  LEFT JOIN postalcode p ON 1 = 1 -- < -- The ON condition makes it a CROSS JOIN

LEFT JOIN 产生的行为 The General Idea/ INNER JOIN

SELECT *
  FROM citizen         c
  LEFT JOIN postalcode p ON c.postal = p.postal
 WHERE p.postal IS NOT NULL -- < -- removed the row where there's no mathcing result from postalcode

维恩图的麻烦

在互联网上搜索“sql join cross inside external”将显示大量维恩图。 我的桌子上曾经放过一份印刷版。 但代表性存在问题。

维恩图非常适合集合论,其中元素可以位于一个或两个集合中。 但对于数据库来说,一个“集合”中的元素在我看来似乎是表中的一行,因此也不会出现在任何其他表中。 不存在一行出现在多个表中的情况。 行对于表来说是唯一的。

自连接是一种极端情况,其中两个集合中的每个元素实际上是相同的。 但它仍然无法摆脱以下任何问题。

集合 A 代表左边的集合(citizen 表),集合 B 代表右边的集合(citizen 表)。邮政编码表)在下面的讨论中。

交叉连接

两个集合中的每个元素都与另一个集合中的每个元素相匹配,这意味着我们需要每个 B 元素的 A 数量和 B 数量每个 A 元素都可以正确表示此笛卡尔积。 集合论不适用于集合中的多个相同元素,因此我发现维恩图正确表示它是不切实际/不可能的。 UNION 似乎根本不适合。

行是不同的。 UNION 总共 7 行。 但它们与常见的 SQL 结果集不兼容。 这根本不是 CROSS JOIN 的工作方式:

CrossJoinUnion1

尝试像这样表示它:

CrossJoinUnion2Crossing

..但现在它看起来就像一个INTERSECTION,它肯定是不。 此外,INTERSECTION 中没有任何元素实际上属于两个不同集合中的任何一个。 但是,它看起来非常像与此类似的可搜索结果:

CrossJoinUnionUnion3

参考 CROSS JOIN 的一个可搜索结果可以在 Tutorialgateway 中查看。 就像这个一样,INTERSECTION 是空的。

INNER JOIN

元素的值取决于JOIN 条件。 可以在每行对于该条件都变得唯一的条件下表示这一点。 含义 id=x 仅适用于一行。 一旦表Acitizen)中的一行与Bpostalcode)中表中的多行匹配, >JOIN 条件,结果与 CROSS JOIN 存在相同的问题:行需要多次表示,而集合论并不是真正为此而设计的。 在唯一性的情况下,该图可以工作,但请记住,JOIN 条件决定了元素在图中的位置。 仅查看 JOIN 条件的值以及行的其余部分:

 InnerJoinIntersection - Filled

当使用 INNER JOINON 1 = 1 条件使其成为 CROSS JOIN 时,此表示完全分崩离析。

使用自JOIN,两个表中的行实际上是相同的元素,但将表表示为AB 则不然非常合适。 例如,使 A 中的元素与 B 中的不同元素匹配的常见自JOIN条件是ON A。 Parent = B.child,在单独的元素上进行从 AB 的匹配。 从示例来看,SQL 如下所示:

SELECT *
  FROM citizen c1
  JOIN citizen c2 ON c1.id = c2.leader

SelfJoinResult

含义 Smith 是两者的领导者格林和詹森。

外连接

当一行与另一表中的行有多个匹配时,麻烦又开始了。 这更加复杂,因为OUTER JOIN 可以被认为是与空集匹配。 但在集合论中,任何集合 C 和空集的并集始终只是 C。 空集不添加任何内容。 此 LEFT OUTER JOIN 的表示通常仅显示所有 A 以说明无论是否存在匹配,都会选择 A 中的行或不是来自 B。 然而,“匹配元素”具有与上图相同的问题。 它们取决于具体情况。 空集似乎已经徘徊到 A

LeftJoinIntersection - Filled

WHERE 子句 - 制作sense

通过与 Smith 和月球邮政编码的 CROSS JOIN 查找所有行:

SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE c.name = 'Smith'
   AND p.area = 'Moon';

Where - result

现在,维恩图不用于反映JOIN。 它用于 WHERE 子句:

Where

..and这就说得通了。

当 INTERSECT 和 UNION 有意义时

INTERSECT

正如所解释的,INNER JOIN 并不是真正的 INTERSECT。 但是,INTERSECT 可以用于单独查询的结果。 在这里,维恩图是有意义的,因为来自单独查询的元素实际上是只属于其中一个结果或同时属于两个结果的行。 Intersect 显然只会返回两个查询中都存在该行的结果。 此 SQL 将生成与上面 WHERE 相同的行,并且维恩图也将相同:

SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE c.name = 'Smith'
INTERSECT
SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE p.area = 'Moon';

UNION

一个 OUTER JOIN 是不是UNION。 然而,UNION 的工作条件与 INTERSECT 相同,导致返回结合两个 SELECT 的所有结果:

SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE c.name = 'Smith'
UNION
SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE p.area = 'Moon';

这相当于:

SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE c.name = 'Smith'
   OR p.area = 'Moon';

..并给出结果:

Union - Result

这里维恩图也有意义:

UNION

当它不适用时

重要说明是,这些仅当两个 SELECT 的结果结构相同时才有效,启用比较或联合。 这两个结果将无法实现:

SELECT *
  FROM citizen
 WHERE name = 'Smith'
SELECT *
  FROM postalcode
 WHERE area = 'Moon';

..尝试将结果与 UNION 结合起来会给出一个

ORA-01790: expression must have same datatype as corresponding expression

为了进一步感兴趣,请阅读在解释 JOIN 时对维恩图说“不”SQL 连接为维恩图。 两者还涵盖EXCEPT

The General Idea

Please see the answer by Martin Smith for a better illustations and explanations of the different joins, including and especially differences between FULL OUTER JOIN, RIGHT OUTER JOIN and LEFT OUTER JOIN.

These two table form a basis for the representation of the JOINs below:

Basis

CROSS JOIN

CrossJoin

SELECT *
  FROM citizen
 CROSS JOIN postalcode

The result will be the Cartesian products of all combinations. No JOIN condition required:

CrossJoinResult

INNER JOIN

INNER JOIN is the same as simply: JOIN

InnerJoin

SELECT *
  FROM citizen    c
  JOIN postalcode p ON c.postal = p.postal

The result will be combinations that satisfies the required JOIN condition:

InnerJoinResult

LEFT OUTER JOIN

LEFT OUTER JOIN is the same as LEFT JOIN

LeftJoin

SELECT *
  FROM citizen         c
  LEFT JOIN postalcode p ON c.postal = p.postal

The result will be everything from citizen even if there are no matches in postalcode. Again a JOIN condition is required:

LeftJoinResult

Data for playing

All examples have been run on an Oracle 18c. They're available at dbfiddle.uk which is also where screenshots of tables came from.

CREATE TABLE citizen (id      NUMBER,
                      name    VARCHAR2(20),
                      postal  NUMBER,  -- <-- could do with a redesign to postalcode.id instead.
                      leader  NUMBER);

CREATE TABLE postalcode (id      NUMBER,
                         postal  NUMBER,
                         city    VARCHAR2(20),
                         area    VARCHAR2(20));

INSERT INTO citizen (id, name, postal, leader)
              SELECT 1, 'Smith', 2200,  null FROM DUAL
        UNION SELECT 2, 'Green', 31006, 1    FROM DUAL
        UNION SELECT 3, 'Jensen', 623,  1    FROM DUAL;

INSERT INTO postalcode (id, postal, city, area)
                 SELECT 1, 2200,     'BigCity',         'Geancy'  FROM DUAL
           UNION SELECT 2, 31006,    'SmallTown',       'Snizkim' FROM DUAL
           UNION SELECT 3, 31006,    'Settlement',      'Moon'    FROM DUAL  -- <-- Uuh-uhh.
           UNION SELECT 4, 78567390, 'LookoutTowerX89', 'Space'   FROM DUAL;

Blurry boundaries when playing with JOIN and WHERE

CROSS JOIN

CROSS JOIN resulting in rows as The General Idea/INNER JOIN:

SELECT *
  FROM citizen          c
  CROSS JOIN postalcode p
 WHERE c.postal = p.postal -- < -- The WHERE condition is limiting the resulting rows

Using CROSS JOIN to get the result of a LEFT OUTER JOIN requires tricks like adding in a NULL row. It's omitted.

INNER JOIN

INNER JOIN becomes a cartesian products. It's the same as The General Idea/CROSS JOIN:

SELECT *
  FROM citizen    c
  JOIN postalcode p ON 1 = 1  -- < -- The ON condition makes it a CROSS JOIN

This is where the inner join can really be seen as the cross join with results not matching the condition removed. Here none of the resulting rows are removed.

Using INNER JOIN to get the result of a LEFT OUTER JOIN also requires tricks. It's omitted.

LEFT OUTER JOIN

LEFT JOIN results in rows as The General Idea/CROSS JOIN:

SELECT *
  FROM citizen         c
  LEFT JOIN postalcode p ON 1 = 1 -- < -- The ON condition makes it a CROSS JOIN

LEFT JOIN results in rows as The General Idea/INNER JOIN:

SELECT *
  FROM citizen         c
  LEFT JOIN postalcode p ON c.postal = p.postal
 WHERE p.postal IS NOT NULL -- < -- removed the row where there's no mathcing result from postalcode

The troubles with the Venn diagram

An image internet search on "sql join cross inner outer" will show a multitude of Venn diagrams. I used to have a printed copy of one on my desk. But there are issues with the representation.

Venn diagram are excellent for set theory, where an element can be in one or both sets. But for databases, an element in one "set" seem, to me, to be a row in a table, and therefore not also present in any other tables. There is no such thing as one row present in multiple tables. A row is unique to the table.

Self joins are a corner case where each element is in fact the same in both sets. But it's still not free of any of the issues below.

The set A represents the set on the left (the citizen table) and the set B is the set on the right (the postalcode table) in below discussion.

CROSS JOIN

Every element in both sets are matched with every element in the other set, meaning we need A amount of every B elements and B amount of every A elements to properly represent this Cartesian product. Set theory isn't made for multiple identical elements in a set, so I find Venn diagrams to properly represent it impractical/impossible. It doesn't seem that UNION fits at all.

The rows are distinct. The UNION is 7 rows in total. But they're incompatible for a common SQL results set. And this is not how a CROSS JOIN works at all:

CrossJoinUnion1

Trying to represent it like this:

CrossJoinUnion2Crossing

..but now it just looks like an INTERSECTION, which it's certainly not. Furthermore there's no element in the INTERSECTION that is actually in any of the two distinct sets. However, it looks very much like the searchable results similar to this:

CrossJoinUnionUnion3

For reference one searchable result for CROSS JOINs can be seen at Tutorialgateway. The INTERSECTION, just like this one, is empty.

INNER JOIN

The value of an element depends on the JOIN condition. It's possible to represent this under the condition that every row becomes unique to that condition. Meaning id=x is only true for one row. Once a row in table A (citizen) matches multiple rows in table B (postalcode) under the JOIN condition, the result has the same problems as the CROSS JOIN: The row needs to be represented multiple times, and the set theory isn't really made for that. Under the condition of uniqueness, the diagram could work though, but keep in mind that the JOIN condition determines the placement of an element in the diagram. Looking only at the values of the JOIN condition with the rest of the row just along for the ride:

InnerJoinIntersection - Filled

This representation falls completely apart when using an INNER JOIN with a ON 1 = 1 condition making it into a CROSS JOIN.

With a self-JOIN, the rows are in fact idential elements in both tables, but representing the tables as both A and B isn't very suitable. For example a common self-JOIN condition that makes an element in A to be matching a different element in B is ON A.parent = B.child, making the match from A to B on seperate elements. From the examples that would be a SQL like this:

SELECT *
  FROM citizen c1
  JOIN citizen c2 ON c1.id = c2.leader

SelfJoinResult

Meaning Smith is the leader of both Green and Jensen.

OUTER JOIN

Again the troubles begin when one row has multiple matches to rows in the other table. This is further complicated because the OUTER JOIN can be though of as to match the empty set. But in set theory the union of any set C and an empty set, is always just C. The empty set adds nothing. The representation of this LEFT OUTER JOIN is usually just showing all of A to illustrate that rows in A are selected regardless of whether there is a match or not from B. The "matching elements" however has the same problems as the illustration above. They depend on the condition. And the empty set seems to have wandered over to A:

LeftJoinIntersection - Filled

WHERE clause - making sense

Finding all rows from a CROSS JOIN with Smith and postalcode on the Moon:

SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE c.name = 'Smith'
   AND p.area = 'Moon';

Where - result

Now the Venn diagram isn't used to reflect the JOIN. It's used only for the WHERE clause:

Where

..and that makes sense.

When INTERSECT and UNION makes sense

INTERSECT

As explained an INNER JOIN is not really an INTERSECT. However INTERSECTs can be used on results of seperate queries. Here a Venn diagram makes sense, because the elements from the seperate queries are in fact rows that either belonging to just one of the results or both. Intersect will obviously only return results where the row is present in both queries. This SQL will result in the same row as the one above WHERE, and the Venn diagram will also be the same:

SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE c.name = 'Smith'
INTERSECT
SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE p.area = 'Moon';

UNION

An OUTER JOIN is not a UNION. However UNION work under the same conditions as INTERSECT, resulting in a return of all results combining both SELECTs:

SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE c.name = 'Smith'
UNION
SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE p.area = 'Moon';

which is equivalent to:

SELECT *
  FROM citizen          c
 CROSS JOIN postalcode  p
 WHERE c.name = 'Smith'
   OR p.area = 'Moon';

..and gives the result:

Union - Result

Also here a Venn diagram makes sense:

UNION

When it doesn't apply

An important note is that these only work when the structure of the results from the two SELECT's are the same, enabling a comparison or union. The results of these two will not enable that:

SELECT *
  FROM citizen
 WHERE name = 'Smith'
SELECT *
  FROM postalcode
 WHERE area = 'Moon';

..trying to combine the results with UNION gives a

ORA-01790: expression must have same datatype as corresponding expression

For further interest read Say NO to Venn Diagrams When Explaining JOINs and sql joins as venn diagram. Both also cover EXCEPT.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文