三个表连接除了INNER JOIN之外的连接

Med*_*Man 5 sql t-sql database sql-server sql-server-2005

我正在学习SQL,本周我正在努力学习JOIN.

我已达到可以进行三个表连接的级别,类似于我见过的很多示例.我还在试图弄清楚事情是如何运作的微小细节.我在三个表连接中看到的所有示例仅使用INNER JOINS.LEFT和RIGHT JOIN怎么样?你有没有在三个表连接中使用它们?这是什么意思?

SELECT ~some columns~ FROM ~table name~
LEFT JOIN ~table 2~ ON ~criteria~
INNER JOIN ~table 3~ ON ~criteria~
Run Code Online (Sandbox Code Playgroud)

要么

SELECT ~some columns~ FROM ~table name~
INNER JOIN ~table 2~ ON ~criteria~
LEFT JOIN ~table 3~ ON ~criteria~
Run Code Online (Sandbox Code Playgroud)

要么

SELECT ~some columns~ FROM ~table name~
LEFT JOIN ~table 2~ ON ~criteria~
LEFT JOIN ~table 3~ ON ~criteria~
Run Code Online (Sandbox Code Playgroud)

要么

???

试着尽可能地探索这个空间

Rus*_*Cam 22

是的,我确实使用了所有这三个JOIN,尽管我倾向于使用LEFT (OUTER) JOINs而不是混合LEFT和RIGHT JOIN.我也用FULL OUTER JOINs和CROSS JOINs.

总之,INNER JOIN将结果集仅限制为JOIN条件满足的记录.请考虑以下表格

编辑:我已经重命名了表名并为它们添加了前缀,@以便表变量可以用于任何阅读此答案并希望进行实验的人.

如果您还想在浏览器中尝试这一点,我也将这一切都设置在SQL Fiddle上 ;

@Table1

id | name
---------
1  | One
2  | Two
3  | Three
4  | Four

@Table2

id | name
---------
1  | Partridge
2  | Turtle Doves
3  | French Hens
5  | Gold Rings
Run Code Online (Sandbox Code Playgroud)

SQL代码

DECLARE @Table1 TABLE (id INT PRIMARY KEY CLUSTERED, [name] VARCHAR(25))

INSERT INTO @Table1 VALUES(1, 'One');
INSERT INTO @Table1 VALUES(2, 'Two');
INSERT INTO @Table1 VALUES(3, 'Three');
INSERT INTO @Table1 VALUES(4, 'Four');

DECLARE @Table2 TABLE (id INT PRIMARY KEY CLUSTERED, [name] VARCHAR(25))

INSERT INTO @Table2 VALUES(1, 'Partridge');
INSERT INTO @Table2 VALUES(2, 'Turtle Doves');
INSERT INTO @Table2 VALUES(3, 'French Hens');
INSERT INTO @Table2 VALUES(5, 'Gold Rings');
Run Code Online (Sandbox Code Playgroud)

一个INNER JOINSQL语句,加入了该id字段

SELECT 
    t1.id,
    t1.name,
    t2.name
FROM
    @Table1 t1
INNER JOIN
    @Table2 t2
    ON 
        t1.id = t2.id
Run Code Online (Sandbox Code Playgroud)

结果是

id | name | name
----------------
1  | One  | Partridge
2  | Two  | Turtle Doves
3  | Three| French Hens
Run Code Online (Sandbox Code Playgroud)

A LEFT JOIN将返回一个结果集,其中包含联接左侧表中的所有记录(如果您要将语句写为一个衬管,首先出现的表)和来自右侧表中的字段与join表达式匹配的join,包含在SELECT子句中.缺少的详细信息将填充NULL

SELECT 
    t1.id,
    t1.name,
    t2.name
FROM
    @Table1 t1
LEFT JOIN
    @Table2 t2
    ON 
        t1.id = t2.id
Run Code Online (Sandbox Code Playgroud)

结果是

id | name | name
----------------
1  | One  | Partridge
2  | Two  | Turtle Doves
3  | Three| French Hens
4  | Four | NULL
Run Code Online (Sandbox Code Playgroud)

A RIGHT JOIN与a的逻辑相同,LEFT JOIN但将返回连接右侧的所有记录和左侧与连接表达式匹配的字段,并包含在SELECT子句中.

SELECT 
    t1.id,
    t1.name,
    t2.name
FROM
    @Table1 t1
RIGHT JOIN
    @Table2 t2
    ON 
        t1.id = t2.id
Run Code Online (Sandbox Code Playgroud)

结果是

id | name | name
----------------
1  | One  | Partridge
2  | Two  | Turtle Doves
3  | Three| French Hens
NULL| NULL| Gold Rings
Run Code Online (Sandbox Code Playgroud)

当然,还有一些FULL OUTER JOIN,包括来自两个连接表的记录,并用NULL 填充任何缺失的细节.

SELECT 
    t1.id,
    t1.name,
    t2.name
FROM
    @Table1 t1
FULL OUTER JOIN
    @Table2 t2
    ON 
        t1.id = t2.id
Run Code Online (Sandbox Code Playgroud)

结果是

id | name | name
----------------
1  | One  | Partridge
2  | Two  | Turtle Doves
3  | Three| French Hens
4  | Four | NULL
NULL| NULL| Gold Rings
Run Code Online (Sandbox Code Playgroud)

和a CROSS JOIN(也称为a CARTESIAN PRODUCT),它只是在SELECT一个表的语句中交叉应用字段与来自另一个表的语句中的字段的产物SELECT.请注意,a中没有连接表达式CROSS JOIN

SELECT 
    t1.id,
    t1.name,
    t2.name
FROM
    @Table1 t1
CROSS JOIN
    @Table2 t2
Run Code Online (Sandbox Code Playgroud)

结果是

id | name  | name
------------------
1  | One   | Partridge
2  | Two   | Partridge
3  | Three | Partridge
4  | Four  | Partridge
1  | One   | Turtle Doves
2  | Two   | Turtle Doves
3  | Three | Turtle Doves
4  | Four  | Turtle Doves
1  | One   | French Hens
2  | Two   | French Hens
3  | Three | French Hens
4  | Four  | French Hens
1  | One   | Gold Rings
2  | Two   | Gold Rings
3  | Three | Gold Rings
4  | Four  | Gold Rings
Run Code Online (Sandbox Code Playgroud)

编辑:

想象一下,现在有一个Table3

@Table3

id | name
---------
2  | Prime 1
3  | Prime 2
5  | Prime 3
Run Code Online (Sandbox Code Playgroud)

SQL代码

DECLARE @Table3 TABLE (id INT PRIMARY KEY CLUSTERED, [name] VARCHAR(25))

INSERT INTO @Table3 VALUES(2, 'Prime 1');
INSERT INTO @Table3 VALUES(3, 'Prime 2');
INSERT INTO @Table3 VALUES(5, 'Prime 3');
Run Code Online (Sandbox Code Playgroud)

现在所有三个表都加入了 INNER JOINS

SELECT 
    t1.id,
    t1.name,
    t2.name,
    t3.name
FROM
    @Table1 t1
INNER JOIN
    @Table2 t2
    ON 
        t1.id = t2.id
INNER JOIN
    @Table3 t3
    ON 
        t1.id = t3.id
Run Code Online (Sandbox Code Playgroud)

结果是

id | name | name         | name
-------------------------------
2  | Two  | Turtle Doves | Prime 1
3  | Three| French Hens  | Prime 2
Run Code Online (Sandbox Code Playgroud)

通过认为id为2和3的记录是所有3个表共有的记录,并且也是我们加入每个表的字段,可能有助于理解这个结果.

现在全部三个 LEFT JOINS

SELECT 
    t1.id,
    t1.name,
    t2.name,
    t3.name
FROM
    @Table1 t1
LEFT JOIN
    @Table2 t2
    ON 
        t1.id = t2.id
LEFT JOIN
    @Table3 t3
    ON 
        t1.id = t3.id
Run Code Online (Sandbox Code Playgroud)

结果是

id | name | name         | name
-------------------------------
1  | One  | Partridge    | NULL
2  | Two  | Turtle Doves | Prime 1
3  | Three| French Hens  | Prime 2
4  | Four | NULL         | NULL
Run Code Online (Sandbox Code Playgroud)

Joel的答案是解释这个结果集的一个很好的解释(Table1是基表/原始表).

现在用a INNER JOIN和aLEFT JOIN

SELECT 
    t1.id,
    t1.name,
    t2.name,
    t3.name
FROM
    @Table1 t1
INNER JOIN
    @Table2 t2
    ON 
        t1.id = t2.id
LEFT JOIN
    @Table3 t3
    ON 
        t1.id = t3.id
Run Code Online (Sandbox Code Playgroud)

结果是

id | name | name         | name
-------------------------------
1  | One  | Partridge    | NULL
2  | Two  | Turtle Doves | Prime 1
3  | Three| French Hens  | Prime 2
Run Code Online (Sandbox Code Playgroud)

虽然我们不知道查询优化器将执行操作的顺序,但我们将从上到下查看此查询以了解结果集.INNER JOINTable1和Table2之间的on ID将结果集限制为仅由连接条件满足的那些记录,即我们在第一个示例中看到的三行.然后,这个临时结果集将LEFT JOIN在表1和表之间的ids上编写表3; 表3中有记录,ID为2和3,但不是id 1,因此t3.name字段将包含2和3但不是1的详细信息.


Joe*_*orn 6

连接只是组合表的方法.加入三个表与加入2 ...或200没什么不同.您可以根据需要混合和匹配INNER,[LEFT/RIGHT/FULL] OUTER,甚至CROSS联接.唯一的区别是保留了哪些结果:INNER连接仅保留两侧与表达式匹配的行.OUTER连接根据LEFT/RIGHT/FULL规范选择"origin"表,始终保留origin表中的所有行,并为来自另一端的与表达式不匹配的行提供NULL值.CROSS联接返回双方的所有可能组合.

诀窍在于,因为你正在处理声明性代码而不是更熟悉的迭代,所以诱惑就是试着把它想象成一切都发生了.当你这样做时,你试图绕过整个查询,它会让人感到困惑.

相反,您希望将其视为连接按顺序发生,从列出的第一个表到最后一个表.这实际上不是它的工作原理,因为查询优化器可以重新排序以使它们运行得更快.但它使开发人员更容易构建查询.

因此,对于三个表,从基表开始,然后从下一个表和下一个表中加入所需的值,依此类推,就像向函数添加代码行以生成所需的输出一样.

至于使用不同的连接类型,我使用了我在这里列出的所有不同类型:INNER,LEFT OUTER,RIGHT OUTER,FULL OUTER和CROSS.但大多数你只需要偶尔使用.INNER JOIN和LEFT JOIN可能会覆盖你想做的95%或更多.

现在让我们来谈谈性能.通常情况下,列出表格的顺序是由您决定的:您从一开始就TableA需要先列出TableB,以便能够访问加入所需的列TableC.但有时两者TableBTableC只取决于TableA,你可以以任何顺序列出.当发生这种情况时,查询优化器通常会为您选择最佳订单,但有时它不知道如何.即使它确实如此,它也有助于建立一个良好的列表表系统,这样你就可以随时查看查询并知道它是"正确的".

考虑到这一点,您应该working set在查询构建时考虑当前内存中的查询.当您开始时TableA,数据库将查看TableA选择列表中的所有列或查询中的任何其他列(如WHERE或ORDER BY子句或潜在索引),WHERE子句中相关条件中的因子,并加载最小部分将该表放入可以逃脱的内存中.它依次为每个表执行此操作,始终尽可能少地加载.这就是关键:你希望尽可能长时间地保持这个工作集尽可能小.

因此,回到我们的三表连接,我们希望按顺序列出表,以使工作集更小更长.这意味着将较小的表列在较大的表上方.另一个好的经验法则是INNER连接倾向于缩小结果集,而OUTER连接则倾向于增加结果集,因此您希望首先列出INNER连接.但是,这不是查询工作的要求,也不是总是如此; 有时反过来也会发生.

最后,我想再次指出,这不是它真正起作用的方式.查询优化器和执行计划是一个非常复杂的主题,数据库可以采取许多技巧来不时破坏这个模型.它只是您作为开发人员可以用来帮助理解服务器正在做什么的一个模型,并帮助您编写更好的查询.