总体思路
请参阅Martin Smith 的answer 以获得对不同连接的更好说明和解释,尤其是FULL OUTER JOIN、RIGHT OUTER JOIN 和LEFT OUTER JOIN 之间的区别。
这两个表构成了下面JOINs 表示的基础:
交叉连接
SELECT *
FROM citizen
CROSS JOIN postalcode
结果将是所有组合的笛卡尔积。不需要JOIN 条件:
内连接
INNER JOIN 等同于:JOIN
SELECT *
FROM citizen c
JOIN postalcode p ON c.postal = p.postal
结果将是满足所需JOIN 条件的组合:
左外连接
LEFT OUTER JOIN 与LEFT JOIN 相同
SELECT *
FROM citizen c
LEFT JOIN postalcode p ON c.postal = p.postal
结果将是来自citizen 的所有内容,即使postalcode 中没有匹配项。再次需要JOIN 条件:
播放数据
所有示例均在 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;
使用JOIN 和WHERE 时边界模糊
交叉连接
CROSS JOIN 导致行为 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
使用CROSS JOIN 获得LEFT OUTER JOIN 的结果需要一些技巧,例如添加NULL 行。省略了。
内连接
INNER JOIN 成为笛卡尔积。和The General Idea一样/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 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 inner external”上的图像互联网搜索将显示大量维恩图。我曾经在我的桌子上放了一份印刷版。但表示存在问题。
维恩图非常适合集合论,其中一个元素可以在一个或两个集合中。但是对于数据库,在我看来,一个“集合”中的元素似乎是表中的一行,因此也不存在于任何其他表中。多个表中不存在一行。一行对表来说是唯一的。
自连接是一种极端情况,其中每个元素实际上在两个集合中都是相同的。但它仍然存在以下任何问题。
集合A 代表左边的集合(citizen 表),集合B 是右边的集合(postalcode 表)在下面的讨论中。
交叉连接
两个集合中的每个元素都与另一个集合中的每个元素匹配,这意味着我们需要每个B 元素的A 数量和每个A 元素的B 数量才能正确表示这个笛卡尔积。集合论不适用于集合中的多个相同元素,因此我发现维恩图正确表示它是不切实际/不可能的。 UNION 似乎根本不适合。
行是不同的。 UNION 总共有 7 行。但它们与常见的SQL 结果集不兼容。这根本不是CROSS JOIN 的工作方式:
试着像这样表示它:
..但现在它看起来就像一个INTERSECTION,它肯定不是。此外,INTERSECTION 中没有任何元素实际上位于两个不同集合中的任何一个中。但是,它看起来很像类似这样的可搜索结果:
作为参考,CROSS JOINs 的一个可搜索结果可以在Tutorialgateway 上看到。 INTERSECTION,就像这个一样,是空的。
内连接
元素的值取决于JOIN 条件。在每一行都对该条件唯一的条件下表示这一点是可能的。含义 id=x 仅适用于 one 行。一旦A(citizen)表中的一行在JOIN条件下匹配B(postalcode)表中的多行,结果与CROSS JOIN有同样的问题:多次表示,而集合论并不是为此而生的。在唯一性条件下,图表可以工作,但请记住,JOIN 条件决定了元素在图表中的位置。仅查看 JOIN 条件的值以及该行的其余部分以进行乘车:
当使用带有ON 1 = 1 条件的INNER JOIN 使其变成CROSS JOIN 时,这种表示完全崩溃了。
使用 self-JOIN,行实际上是两个表中的相同元素,但将表同时表示为 A 和 B 不是很合适。例如,一个常见的 self-JOIN 条件使 A 中的元素匹配 B 中的 不同 元素是 ON A.parent = B.child,使得匹配从 A 到 B单独的元素。从像这样的SQL 示例中:
SELECT *
FROM citizen c1
JOIN citizen c2 ON c1.id = c2.leader
意思是史密斯是格林和詹森的领袖。
外连接
当一行与另一表中的行有多个匹配时,麻烦又开始了。这更加复杂,因为OUTER JOIN 可以被认为与空集匹配。但是在集合论中,任何集合C 和一个空集的并集总是只是C。空集什么也没增加。这个LEFT OUTER JOIN 的表示通常只显示所有A,以说明A 中的行被选中,而不管B 是否匹配。然而,“匹配元素”具有与上图相同的问题。它们取决于条件。而空集似乎已经飘到A:
WHERE 子句 - 有意义
从CROSS JOIN 中查找所有行,其中包含 Smith 和月球上的邮政编码:
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE c.name = 'Smith'
AND p.area = 'Moon';
现在维恩图不用于反映JOIN。它仅用于WHERE 子句:
..这是有道理的。
当 INTERSECT 和 UNION 有意义时
相交
正如所解释的,INNER JOIN 并不是真正的INTERSECT。但是INTERSECTs 可以用于单独查询的结果。这里的维恩图很有意义,因为来自单独查询的元素实际上是属于一个结果或两者的行。相交显然只会返回两个查询中都存在该行的结果。这个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';
联合
OUTER JOIN 不是UNION。但是UNION 在与INTERSECT 相同的条件下工作,导致返回结合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';
相当于:
SELECT *
FROM citizen c
CROSS JOIN postalcode p
WHERE c.name = 'Smith'
OR p.area = 'Moon';
..并给出结果:
这里的维恩图也很有意义:
当它不适用时
重要说明是,这些仅在两个 SELECT 的结果结构相同时才有效,从而可以进行比较或联合。这两个的结果将无法实现:
SELECT *
FROM citizen
WHERE name = 'Smith'
SELECT *
FROM postalcode
WHERE area = 'Moon';
..尝试将结果与UNION 结合起来会得到一个
ORA-01790: expression must have same datatype as corresponding expression
更多兴趣请阅读Say NO to Venn Diagrams When Explaining JOINs 和sql joins as venn diagram。两者都涵盖EXCEPT。