CategoryResourceRepost/极客时间专栏/SQL必知必会/第一章:SQL语法基础篇/11丨SQL99是如何使用连接的,与SQL92的区别是什么?.md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

274 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="11丨SQL99是如何使用连接的与SQL92的区别是什么" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a8/2e/a8ee923ce2bb10b7140f6c1c3cadb32e.mp3"></audio>
上节课我们讲解了SQL92标准在它之后又提出了SQL99标准。现在各大DBMS中对SQL99标准的支持度更好。你一定听说过LEFT JOIN、RIGHT JOIN这样的操作符这实际上就是SQL99的标准在SQL92中它们是用+代替的。SQL92和SQL99标准原理类似只是SQL99标准的可读性更强。
今天我就来讲解一下SQL99标准中的连接查询在今天的课程中你需要重点掌握以下几方面的内容
1. SQL99标准下的连接查询是如何操作的
1. SQL99与SQL92的区别是什么
1. 在不同的DBMS中使用连接需要注意什么
## SQL99标准中的连接查询
上一篇文章中我用NBA球员的数据表进行了举例包括了三张数据表player、team和height_grades。
其中player表为球员表一共有37个球员如下所示
<img src="https://static001.geekbang.org/resource/image/ee/99/ee9b554ecbc296e1a5865b52d4bb3c99.png" alt=""><br>
team表为球队表一共有3支球队如下所示
<img src="https://static001.geekbang.org/resource/image/aa/75/aa73203c43672b6d3be44748b1556075.png" alt=""><br>
height_grades表为身高等级表如下所示
<img src="https://static001.geekbang.org/resource/image/4b/37/4b5b2c666705364b793329b728a1ed37.png" alt=""><br>
接下来我们看下在SQL99标准中是如何进行连接查询的
### 交叉连接
交叉连接实际上就是SQL92中的笛卡尔乘积只是这里我们采用的是CROSS JOIN。
我们可以通过下面这行代码得到player和team这两张表的笛卡尔积的结果
```
SQL: SELECT * FROM player CROSS JOIN team
```
运行结果一共37*3=111条记录
<img src="https://static001.geekbang.org/resource/image/95/d2/95c97414eca15373f26ae2b4192880d2.png" alt=""><br>
如果多张表进行交叉连接比如表t1表t2表t3进行交叉连接可以写成下面这样
```
SQL: SELECT * FROM t1 CROSS JOIN t2 CROSS JOIN t3
```
### 自然连接
你可以把自然连接理解为SQL92中的等值连接。它会帮你自动查询两张连接表中所有相同的字段然后进行等值连接。
如果我们想把player表和team表进行等值连接相同的字段是team_id。还记得在SQL92标准中是如何编写的么
```
SELECT player_id, a.team_id, player_name, height, team_name FROM player as a, team as b WHERE a.team_id = b.team_id
```
在SQL99中你可以写成
```
SELECT player_id, team_id, player_name, height, team_name FROM player NATURAL JOIN team
```
实际上在SQL99中用NATURAL JOIN替代了 `WHERE player.team_id = team.team_id`
### ON连接
ON连接用来指定我们想要的连接条件针对上面的例子它同样可以帮助我们实现自然连接的功能
```
SELECT player_id, player.team_id, player_name, height, team_name FROM player JOIN team ON player.team_id = team.team_id
```
这里我们指定了连接条件是`ON player.team_id = team.team_id`相当于是用ON进行了team_id字段的等值连接。
当然你也可以ON连接进行非等值连接比如我们想要查询球员的身高等级需要用player和height_grades两张表
```
SQL99SELECT p.player_name, p.height, h.height_level
FROM player as p JOIN height_grades as h
ON height BETWEEN h.height_lowest AND h.height_highest
```
这个语句的运行结果和我们之前采用SQL92标准的查询结果一样。
```
SQL92SELECT p.player_name, p.height, h.height_level
FROM player AS p, height_grades AS h
WHERE p.height BETWEEN h.height_lowest AND h.height_highest
```
一般来说在SQL99中我们需要连接的表会采用JOIN进行连接ON指定了连接条件后面可以是等值连接也可以采用非等值连接。
### USING连接
当我们进行连接的时候可以用USING指定数据表里的同名字段进行等值连接。比如
```
SELECT player_id, team_id, player_name, height, team_name FROM player JOIN team USING(team_id)
```
你能看出与自然连接NATURAL JOIN不同的是USING指定了具体的相同的字段名称你需要在USING的括号()中填入要指定的同名字段。同时使用JOIN USING可以简化JOIN ON的等值连接它与下面的SQL查询结果是相同的
```
SELECT player_id, player.team_id, player_name, height, team_name FROM player JOIN team ON player.team_id = team.team_id
```
### 外连接
SQL99的外连接包括了三种形式
1. 左外连接LEFT JOIN 或 LEFT OUTER JOIN
1. 右外连接RIGHT JOIN 或 RIGHT OUTER JOIN
1. 全外连接FULL JOIN 或 FULL OUTER JOIN
我们在SQL92中讲解了左外连接、右外连接在SQL99中还有全外连接。全外连接实际上就是左外连接和右外连接的结合。在这三种外连接中我们一般省略OUTER不写。
1.左外连接
**SQL92**
```
SELECT * FROM player, team where player.team_id = team.team_id(+)
```
**SQL99**
```
SELECT * FROM player LEFT JOIN team ON player.team_id = team.team_id
```
2.右外连接
**SQL92**
```
SELECT * FROM player, team where player.team_id(+) = team.team_id
```
**SQL99**
```
SELECT * FROM player RIGHT JOIN team ON player.team_id = team.team_id
```
3.全外连接
**SQL99**
```
SELECT * FROM player FULL JOIN team ON player.team_id = team.team_id
```
需要注意的是MySQL不支持全外连接否则的话全外连接会返回左表和右表中的所有行。当表之间有匹配的行会显示内连接的结果。当某行在另一个表中没有匹配时那么会把另一个表中选择的列显示为空值。
也就是说,全外连接的结果=左右表匹配的数据+左表没有匹配到的数据+右表没有匹配到的数据。
### 自连接
自连接的原理在SQL92和SQL99中都是一样的只是表述方式不同。
比如我们想要查看比布雷克·格里芬身高高的球员都有哪些在两个SQL标准下的查询如下。
**SQL92**
```
SELECT b.player_name, b.height FROM player as a , player as b WHERE a.player_name = '布雷克-格里芬' and a.height &lt; b.height
```
**SQL99**
```
SELECT b.player_name, b.height FROM player as a JOIN player as b ON a.player_name = '布雷克-格里芬' and a.height &lt; b.height
```
运行结果6条记录
<img src="https://static001.geekbang.org/resource/image/c7/e0/c79ecee3e5368ee73bfe7edb8a80a6e0.png" alt="">
## SQL99和SQL92的区别
至此我们讲解完了SQL92和SQL99标准下的连接查询它们都对连接进行了定义只是操作的方式略有不同。我们再来回顾下这些连接操作基本上可以分成三种情况
1. 内连接:将多个表之间满足连接条件的数据行查询出来。它包括了等值连接、非等值连接和自连接。
1. 外连接:会返回一个表中的所有记录,以及另一个表中匹配的行。它包括了左外连接、右外连接和全连接。
1. 交叉连接也称为笛卡尔积返回左表中每一行与右表中每一行的组合。在SQL99中使用的CROSS JOIN。
不过SQL92在这三种连接操作中和SQL99还存在着明显的区别。
首先我们看下SQL92中的WHERE和SQL99中的JOIN。
你能看出在SQL92中进行查询时会把所有需要连接的表都放到FROM之后然后在WHERE中写明连接的条件。而SQL99在这方面更灵活它不需要一次性把所有需要连接的表都放到FROM之后而是采用JOIN的方式每次连接一张表可以多次使用JOIN进行连接。
另外我建议多表连接使用SQL99标准因为层次性更强可读性更强比如
```
SELECT ...
FROM table1
JOIN table2 ON table1和table2的连接条件
JOIN table3 ON table2和table3的连接条件
```
它的嵌套逻辑类似我们使用的FOR循环
```
for t1 in table1:
for t2 in table2:
if condition1:
for t3 in table3:
if condition2:
output t1 + t2 + t3
```
SQL99采用的这种嵌套结构非常清爽即使再多的表进行连接也都清晰可见。如果你采用SQL92可读性就会大打折扣。
最后一点就是SQL99在SQL92的基础上提供了一些特殊语法比如NATURAL JOIN和JOIN USING。它们在实际中是比较常用的省略了ON后面的等值条件判断让SQL语句更加简洁。
## 不同DBMS中使用连接需要注意的地方
SQL连接具有通用性但是不同的DBMS在使用规范上会存在差异在标准支持上也存在不同。在实际工作中你需要参考你正在使用的DBMS文档这里我整理了一些需要注意的常见的问题。
**1.不是所有的DBMS都支持全外连接**
虽然SQL99标准提供了全外连接但不是所有的DBMS都支持。不仅MySQL不支持Access、SQLite、MariaDB等数据库软件也不支持。不过在Oracle、DB2、SQL Server中是支持的。
**2.Oracle没有表别名AS**
为了让SQL查询语句更简洁我们经常会使用表别名AS不过在Oracle中是不存在AS的使用表别名的时候直接在表名后面写上表别名即可比如player p而不是player AS p。
**3.SQLite的外连接只有左连接**
SQLite是一款轻量级的数据库软件在外连接上只支持左连接不支持右连接不过如果你想使用右连接的方式比如`table1 RIGHT JOIN table2`在SQLite你可以写成`table2 LEFT JOIN table1`,这样就可以得到相同的效果。
除了一些常见的语法问题,还有一些关于连接的性能问题需要你注意:
**1.控制连接表的数量**
多表连接就相当于嵌套for循环一样非常消耗资源会让SQL查询性能下降得很严重因此不要连接不必要的表。在许多DBMS中也都会有最大连接表的限制。
**2.在连接时不要忘记WHERE语句**
多表连接的目的不是为了做笛卡尔积而是筛选符合条件的数据行因此在多表连接的时候不要忘记了WHERE语句这样可以过滤掉不必要的数据行返回。
**3.使用自连接而不是子查询**
我们在查看比布雷克·格里芬高的球员都有谁的时候可以使用子查询也可以使用自连接。一般情况建议你使用自连接因为在许多DBMS的处理过程中对于自连接的处理速度要比子查询快得多。你可以这样理解子查询实际上是通过未知表进行查询后的条件判断而自连接是通过已知的自身数据表进行条件判断因此在大部分DBMS中都对自连接处理进行了优化。
## 总结
连接可以说是SQL中的核心操作通过两篇文章的学习你已经从多个维度对连接进行了了解。同时我们对SQL的两个重要标准SQL92和SQL99进行了学习在我们需要进行外连接的时候建议采用SQL99标准这样更适合阅读。
此外我还想强调一下我们在进行连接的时候使用的关系型数据库管理系统之所以存在关系是因为各种数据表之间存在关联它们并不是孤立存在的。在实际工作中尤其是做业务报表的时候我们会用到SQL中的连接操作JOIN因此我们需要理解和熟练掌握SQL标准中连接的使用以及不同DBMS中对连接的语法规范。剩下要做的就是通过做练习和实战来增强你的经验了做的练习多了也就自然有感觉了。
<img src="https://static001.geekbang.org/resource/image/44/5b/443181aea770ba5844efac6b02e02c5b.jpg" alt=""><br>
我今天讲解了SQL99的连接操作不妨请你做一个小练习。请你编写SQL查询语句查询不同身高级别对应height_grades表对应的球员数量对应player表
欢迎你在评论区写下你的答案,我会在评论区与你一起讨论。也欢迎把这篇文章分享给你的朋友或者同事。