MySQL 联表查询
MySQL 联表查询
关系型数据库,免不了表之间存在各种引用与关联。这些关联是通过主键与外键搭配来形成的。所以,取数据时,很大情况下单张表无法满足需求,额外的数据则需要将其他表加入到查询中来,这便是 JOIN
关键字完成的操作。
- MySQL 中
JOIN
,CROSS JOIN
和INNER JOIN
三者语法功能上相同,可互换,而 SQL 标准中,INNER JOIN
需要搭配ON
语句。
多表联合查询时,可省略 JOIN 关键字,以逗号分隔多张表,此时默认会当作 INNER JOIN 来处理。比如,
SELECT table1.*,
table2.*
FROM table1,
table2;
等效于:
SELECT table1.*,
table2.*
FROM table1
INNER JOIN table2;
- 但这种通过逗号隐式指定的联表形式其优先级要低于直接通过关键字(
INNER JOIN
,CROSS JOIN
,LEFT JOIN
)指定的形式。所以t1, t2 JOIN t3
会被解析成(t1, (t2 JOIN t3))
而不是((t1, t2) JOIN t3)
需要注意的是,当逗号形式与其他联表关键词结合时,在指定了联表条件,比如通过 ON
条件时,会报错。
-
ON
指定的联表条件其语法同WHERE
,所有后者可接受的表达式都可用于ON
。两者看起来功能上雷同,ON
一般用于指定联表条件,即表之间怎么被联合,而WHERE
则用于过滤结果。 -
LEFT JOIN
时,右边表中不满足ON
或USING
指定的条件时,会在结果中以NULL
呈现。
SELECT left_tbl.*
FROM left_tbl LEFT JOIN right_tbl ON left_tbl.id = right_tbl.id
WHERE right_tbl.id IS NULL;
通过此方法可方便地过滤出右边表中不符合条件的记录。
- 联表查询时可为每张参与进来的表指定别名,方便在其他表达式中引用。两种方式,一个是通过
AS
关键字tbl_name AS alias_name
,另一种是直接在表名后面跟上别名,tbl_name alias_name
。
SELECT t1.name, t2.salary
FROM employee AS t1 INNER JOIN info AS t2 ON t1.name = t2.name;
SELECT t1.name, t2.salary
FROM employee t1 INNER JOIN info t2 ON t1.name = t2.name;
一条查询语句中的子查询必需取一个别名,这样才能在其他表达式中引用。
SELECT * FROM (SELECT 1, 2, 3) AS t1;
USING(join_column_list)
语句指定两个表中均包含的列,查询时只针对这里指定的列进行比较。
a LEFT JOIN b USING (c1, c2, c3)
-
NATURAL [LEFT] JOIN
与INNER JOIN
和LEFT JOIN
配合使用了USING
指定表中所有列的情况等效。 -
RIGHT JOIN
与LEFT JOIN
类似,只是最终结果是依据右边表,将左边表中不符合的在结果中以 NULL 呈现。为了方便在不同数据库间迁移,推荐始终使用LEFT JOIN
。
一些 JOIN
示例:
SELECT * FROM table1, table2;
SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id;
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id;
SELECT * FROM table1 LEFT JOIN table2 USING (id);
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id
LEFT JOIN table3 ON table2.id = table3.id;
NATURAL JOIN
的结果中不会有重复的列。因为其与USING
雷同,所以USING
时也没有复杂的列。
考察下面的示例:
CREATE TABLE t1 (i INT, j INT);
CREATE TABLE t2 (k INT, j INT);
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t2 VALUES(1, 1);
SELECT * FROM t1 NATURAL JOIN t2;
SELECT * FROM t1 JOIN t2 USING (j);
查询结果:
+------+------+------+
| j | i | k |
+------+------+------+
| 1 | 1 | 1 |
+------+------+------+
+------+------+------+
| j | i | k |
+------+------+------+
| 1 | 1 | 1 |
+------+------+------+
结果中同名的列只出现一次,且都是值相同的那些记录。
通过向两表中插入一条新记录,令它们的 j
不相同,再进行测试。
mysql> INSERT INTO t1 VALUES(2, 2);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO t2 VALUES(2, 3);
Query OK, 1 row affected (0.00 sec)
mysql> select * from t1 natural join t2;
+------+------+------+
| j | i | k |
+------+------+------+
| 2 | 2 | 1 |
+------+------+------+
1 row in set (0.00 sec)
USING
和ON
作为条件时其他限制的联合条件是一样的,可互相转换。但在SELECT *
返回结果时,还是有差异的。前者只在USING
中指定的列中返回合并后的结果,后者则针对的是表中所有列。
a LEFT JOIN b USING (c1, c2, c3)
a LEFT JOIN b ON a.c1 = b.c1 AND a.c2 = b.c2 AND a.c3 = b.c3
USING
情况下的返回:
COALESCE(a.c1, b.c1), COALESCE(a.c2, b.c2), COALESCE(a.c3, b.c3)
ON
的返回:
a.c1, a.c2, a.c3, b.c1, b.c2, b.c3
ON
语句中只能引用其操作表(operands)中的表。
CREATE TABLE t1 (i1 INT);
CREATE TABLE t2 (i2 INT);
CREATE TABLE t3 (i3 INT);
针对上面的表,以下查询会报错:
mysql> SELECT * FROM t1 JOIN t2 ON (i1 = i3) JOIN t3;
ERROR 1054 (42S22): Unknown column 'i3' in 'on clause'
而以下查询则可以:
mysql> SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i3);
Empty set (0.00 sec)
因为此时 t3
在 ON
语句的操作范围内了。