SQL学习指南——再谈连接
外连接
到目前为止,所有的示例都包括多个数据表,我们并没有考虑过连接条件可能无法为数据表中的所有行找到匹配,例如,inventory数据表中的每行包含的都是一部可供租借的电影,但是film数据表中的1000部电影(1000行)中只有958部在inventory数据表中有一行或多行,其余42部电影不能用于租借,所以这些电影的ID无法在inventory数据表中找到,下列查询通过连接这两个数据表,统计每部电影可用的拷贝数量
由于查询使用的是内连接,只返回满足连接条件的行,所以只返回了958行,如果希望查询返回所有的1000部电影,而不管在inventory数据表中有没有对应的行,那么可以使用外连接,使连接条件成为可选的
从上述结果中可以看出,该查询返回了film数据表的全部1000行
下面描述了对该查询的改动:
- 将连接定义从inner改为left outer,指示服务器包含该连接左侧数据表的所有行
- cnt列的定义从count(*)改为count(i.inventory_id),后者统计inventory.inventory_id列值为非null的数量
左外连接与右外连接
计算left outer join结果集的行数,核心逻辑就是看左表的每一行在右表“命中”了多少次
可以把它想象成一次逐行的配对过程,数据库会从左表拿出第一行数据,去右表中寻找所有符合连接条件(on后面的规则)的记录
如果右表有N条记录能和它匹配,这一行就会在结果集中产生N条结果
如果右表一条都匹配不上(也就是0条),为了保证左表数据不丢失,它会强行产生1条结果,只不过右边对应的字段全是null
因此,结果集的总行数可以用一个简单的公式来概括
结果集总行数 = Σ (左表每行在右表中的实际匹配次数)
注意:这里的实际匹配次数有一个保底规则,最少算作1次
在上面的示例中指定的是left outer join,也可以指定right outer join
这两个查询执行的都是外连接。因为很少(如果有的话)会遇到右外连接,而且也不是所有的数据库服务器都支持这种连接,因此推荐使用左外连接,outer关键字是可选的,不过出于清晰性的考虑,最好还是加上outer
交叉连接
笛卡尔积本质上就是在未指定任何连接条件的情况下的多数据表连接的结果,笛卡尔集经常会偶然用到(比如,忘记在from子句中添加连接条件),但是使用频率并不高,如果确实打算生成两个数据表的笛卡尔积,应该指定交叉连接(cross join)
该查询生成数据表category和language的笛卡尔积,共计96行(category数据表16行 * language数据表6行)
自然连接
可以选择一种连接类型,其允许命名要连接的数据表,但是由数据库服务器决定需要什么样的连接条件,这种连接类型被称为自然连接(natural join),它依靠多个数据表之间相同的列名来推断适合的连接条件
核心逻辑:自动寻找两张表中同名的列,强制让它们相等进行匹配,并且在最终结果里把重复的列合并成一份
如果两张表中有两个或更多同名的列,自然连接(Natural Join)会触发一个非常隐蔽的“连坐”机制:它会把所有的同名列都自动加入连接条件,并且要求这些列的值必须同时相等
假设我们有两个表:员工表(employees) 和 部门表(departments)。
它们原本通过 dept_id(部门ID)关联,但刚好两张表里都有一个同名的记录时间的字段 created_at(创建时间)。
当你执行 SELECT * FROM employees NATURAL JOIN departments; 时,数据库在后台默默执行的逻辑等价于:
SELECT * FROM employees INNER JOIN departments ON employees.dept_id=departments.dept_id AND employees.created_at=departments.created_at;-- 👈 灾难现场!那么,为了省事而不输入连接条件到底值不值得呢?绝对不值得,应该避免使用这种连接类型,而使用带有显式连接条件的内连接
