PHP课程 - 第3.6课 - 使用MySQL数据库。JOIN操作符的类型。
在 MySQL 中,可以通过多种方式使用 JOIN 进行数据选择。我们将尝试逐一讲解所有这些类型的查询。以下是所有涉及 JOIN 的查询列表:
- INNER JOIN(内连接)
- LEFT JOIN(左连接)
- LEFT JOIN(不与右表交集的左连接)
- RIGHT JOIN(右连接)
- RIGHT JOIN(不与左表交集的右连接)
- FULL OUTER JOIN(全外连接)
- FULL OUTER JOIN(左表或右表为空时)
以下是这些 JOIN 类型的示意图:
我将把我们网站的文件附加到文章中,其中包括 join.php 文件,我将在其中使用不同的 JOIN 操作符来输出所有记录。
INNER JOIN(内连接)
我们从 INNER JOIN 开始,因为如果在查询中仅写 JOIN,它默认就是 INNER JOIN。该操作符从两张表中选取满足 ON 条件的所有记录。我们有两张表:Files 和 Messages:
Messages 表:
mid | bodytext | fid |
1 | Test | NULL |
2 | Hi | 2 |
3 | Hello | NULL |
Files 表:
fid | path |
1 | /files/1.png |
2 | /files/2.png |
3 | /files/3.png |
JOIN 查询如下:
SELECT * FROM Messages INNER JOIN Files ON Messages.fid=Files.fid
结果如下:
mid | bodytext | fid | path |
2 | Hi | 2 | /files/2.png |
也就是说,只有当 fid 相同时,MySQL 才会输出这些行。
LEFT JOIN(左连接)
使用 LEFT JOIN 时,我们输出左表(此处为 Messages)中的所有记录,包括那些在右表(Files)中存在匹配 fid 的记录。
Messages 表:
mid | bodytext | fid |
1 | Test | 2 |
2 | Hi | NULL |
3 | Hello | 3 |
Files 表:
fid | path |
1 | /files/1.png |
2 | /files/2.png |
3 | /files/3.png |
LEFT JOIN 查询如下:
SELECT * FROM Messages LEFT JOIN Files ON Messages.fid=Files.fid
结果如下:
mid | bodytext | fid | path |
1 | Test | 2 | /files/2.png |
2 | Hi | NULL | NULL |
3 | Hello | 3 | /files/3.png |
当我们需要输出所有消息记录时,无论是否有附加文件,我们都可以使用 LEFT JOIN,然后通过 PHP 来检测文件是否存在。
LEFT JOIN(不与右表交集的左连接)
LEFT JOIN 会输出左表中所有记录,除了 fid 在右表中有匹配的那些。
Messages 表:
mid | bodytext | fid |
1 | Test | 2 |
2 | Hi | NULL |
3 | Hello | 3 |
Files 表:
fid | path |
1 | /files/1.png |
2 | /files/2.png |
3 | /files/3.png |
不与右表交集的 LEFT JOIN 查询如下:
SELECT * FROM Messages LEFT JOIN Files ON Messages.fid=Files.fid WHERE Files.fid IS NULL
结果如下:
mid | bodytext | fid | path |
2 | Hi | NULL | NULL |
这种 LEFT JOIN 用于输出所有没有附加文件的记录。
RIGHT JOIN(右连接)
RIGHT JOIN 输出右表中的所有记录,如果有匹配项,则显示来自左表的数据。
Messages 表:
mid | bodytext | fid |
1 | Test | 2 |
2 | Hi | NULL |
3 | Hello | 3 |
Files 表:
fid | path |
1 | /files/1.png |
2 | /files/2.png |
3 | /files/3.png |
RIGHT JOIN 查询如下:
SELECT * FROM Messages RIGHT JOIN Files ON Messages.fid=Files.fid
结果如下:
mid | bodytext | fid | path |
NULL | NULL | 1 | /files/1.png |
1 | Test | 2 | /files/2.png |
3 | Hello | 3 | /files/3.png |
RIGHT JOIN 用于输出所有文件,不论它们是否被使用。
RIGHT JOIN(不与左表交集的右连接)
这种 RIGHT JOIN 输出右表中所有记录,除了那些与左表有交集的。
Messages 表:
mid | bodytext | fid |
1 | Test | 2 |
2 | Hi | NULL |
3 | Hello | 3 |
Files 表:
fid | path |
1 | /files/1.png |
2 | /files/2.png |
3 | /files/3.png |
查询如下:
SELECT * FROM Messages RIGHT JOIN Files ON Messages.fid=Files.fid WHERE Messages.fid IS NULL
结果如下:
mid | bodytext | fid | path |
NULL | NULL | 1 | /files/1.png |
这种 RIGHT JOIN 用于输出未附加到任何消息的文件,例如我们想找出未被使用的文件。
FULL OUTER JOIN(全外连接)
虽然 SQL 语言中有 FULL OUTER JOIN,但 MySQL 并不支持。这是因为这种操作符会对服务器造成较大负担。目前我们只有 3 个文件和 3 条消息,执行后会生成 4 行结果。虽然不建议,但我们可以用 LEFT JOIN 与 RIGHT JOIN 的并集(UNION)来模拟 FULL OUTER JOIN。
Messages 表:
mid | bodytext | fid |
1 | Test | 2 |
2 | Hi | NULL |
3 | Hello | 3 |
Files 表:
fid | path |
1 | /files/1.png |
2 | /files/2.png |
3 | /files/3.png |
FULL OUTER JOIN 的模拟查询如下:
SELECT * FROM Messages LEFT JOIN Files ON Messages.fid = Files.fid UNION SELECT * FROM Messages RIGHT JOIN Files ON Messages.fid = Files.fid
在该查询中,我们使用 UNION 将 LEFT JOIN 与 RIGHT JOIN 的结果合并。
结果如下:
mid | bodytext | fid | path |
1 | Test | 2 | /files/2.png |
2 | Hi | NULL | NULL |
3 | Hello | 3 | /files/3.png |
NULL | NULL | 1 | /files/1.png |
至于 FULL OUTER JOIN 的实际用途,虽然在 SQL 中存在,但在日常应用中并不常见。
FULL OUTER JOIN(不与任一表交集)
还有一种比 FULL OUTER JOIN 更极端的 JOIN 类型,即 FULL OUTER JOIN 不与任一表交集的版本。我几乎想不到在哪种情况下会使用这种 JOIN,因为结果包含未附加文件的消息与未被使用的文件。而且 MySQL 同样不支持 这种 JOIN,我们只能通过两个无交集的 LEFT JOIN 与 RIGHT JOIN 来模拟。
模拟查询如下:
$sql = 'SELECT * FROM Messages LEFT JOIN Files ON Messages.fid = Files.fid WHERE Files.fid IS NULL UNION SELECT * FROM Messages RIGHT JOIN Files ON Messages.fid = Files.fid WHERE Messages.fid IS NULL';
使用与上例相同的数据,我们将得到:
mid | bodytext | fid | path |
2 | Hi | NULL | NULL |
NULL | NULL | 1 | /files/1.png |
这大概就是全部内容了,在接下来的课程中,我们将开始编写更复杂的跨多表查询。