源自MySQL 5.7 官方手册:13.2.9.2 JOIN Syntaxhtml
SELECT select_expr From table_references JOIN... WHERE...
如上所示,MySQL支持在table_references后添加JOIN选项做为SELECT语句的一部分,固然也能够在多表的DELETE和UPDATE。mysql
下面列出了JOIN的详细语法:sql
table_references: escaped_table_reference [, escaped_table_reference] ... escaped_table_reference: table_reference | { OJ table_reference } table_reference: table_factor | joined_table table_factor: tbl_name [PARTITION (partition_names)] [[AS] alias] [index_hint_list] | table_subquery [AS] alias | ( table_references ) joined_table: table_reference [INNER | CROSS] JOIN table_factor [join_specification] | table_reference STRAIGHT_JOIN table_factor | table_reference STRAIGHT_JOIN table_factor ON search_condition | table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_specification | table_reference NATURAL [{LEFT|RIGHT} [OUTER]] JOIN table_factor join_specification: ON search_condition | USING (join_column_list) join_column_list: column_name [, column_name] ... index_hint_list: index_hint [, index_hint] ... index_hint: USE {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] ([index_list]) | {IGNORE|FORCE} {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] (index_list) index_list: index_name [, index_name] ...
1、表引用(table reference)数据库
一个表引用也被称为一个JOIN表达式。表引用(当它引用分区表时)可能有PARTITION选项,包括一个由逗号分隔的分区,子分区或二者皆有的列表。此选项紧跟在的名字以后,并在任何别名声明以前。此选项的做用是仅从列出的分区或子分区中选择数据行,并且将忽略列表中未命名的任何分区或子分区。see Section 22.5, “Partition Selection”。测试
table_factor语法是MySQL对标准SQL中的扩展。标准SQL只接受table_reference,而不是一对括号内的列表。优化
若是table_reference项列表中的每一个逗号被视为内链接(INNER JOIN),则这是保守的扩展。例如:spa
SELECT * FROM t1 LEFT JOIN (t2, t3, t4) ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)
等价于:code
SELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4) ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)
在MySQL中,JOIN,CROSS JOIN,和INNER JOIN 在语义上是等价的,他们能够相互替换。可是在标准SQL中,他们不等价,INNER JOIN与ON搭配使用,CROSS JOIN搭配其它。htm
通常来讲,在只有INNER JOIN操做的表达式中,括号能够被省略。MySQL还支持嵌套链接,blog
See Section 8.2.1.7, “Nested Join Optimization”。
指定索引提示(Index hints )可以影响MySQL优化器如何使用索引。更多信息,see Section 8.9.4, “Index Hints”.
优化器提示和optimizer_switch系统变量是影响优化器使用索引的其余方法。See Section 8.9.3, “Optimizer Hints”, and Section 8.9.2, “Switchable Optimizations”。
2、在编写联接时要考虑的通常因素
2.1
可使用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;
2.2
table_subquery也称为FROM子句中的派生表或子查询。Section 13.2.10.8, “Derived Tables”.
此类子查询必须包含别名,以便为子查询结果提供表名。一个简单的例子以下:
SELECT * FROM (SELECT 1, 2, 3) AS t1; /* +---+---+---+ | 1 | 2 | 3 | +---+---+---+ | 1 | 2 | 3 | +---+---+---+ */
2.3
在没有链接条件的状况下,INNER JOIN和“,”(逗号)在语义上是等效的——二者都在指定的表之间产生笛卡尔积,也就是说,第一个表中的每一行都链接到第二个表中的每一行。
可是,逗号运算符的优先级比其它含有“JOIN”的运算符要小。若是在存在链接条件时将逗号链接与其余链接类型混合,则可能会报错:Unknown column 'col_name' in 'on clause' 。对这个问题的处理会在文章的后面讨论。
与ON一块儿使用的search_condition是能够在WHERE子句中使用的任何条件表达式。ON子句用于指明如多表如何链接,WHERE子句则限制要包含在结果集中的行。
2.4
在LEFT JOIN中,若是在右表中没有匹配ON或者USING中条件的行,则该链接中中的右表的列全都设置为NULL。你能够利用这点来查找左表A中在右表B中没有任何对应项的行:
SELECT left_tbl.* FROM left_tbl LEFT JOIN right_tbl ON left_tbl.id = right_tbl.id WHERE right_tbl.id IS NULL;
这个查询语句会找出左表left_tbl中这样的行:其ID值在右表right_tbl的ID列中不存在。See Section 8.2.1.8, “Outer Join Optimization”.(外链接包括LEFT JOIN和RIGHT JOIN)
例如,我查找学生表stu中在成绩表sc中没有任何成绩的学生:
select stu.*
from student as stu left join sc on stu.SId=sc.SId
where sc.SId is null; /* SId | Sname | Sage | Ssex | +------+-------+---------------------+------+ | 09 | 张三 | 2017-12-20 00:00:00 | 女 | | 10 | 李四 | 2017-12-25 00:00:00 | 女 | | 11 | 李四 | 2017-12-30 00:00:00 | 女 | | 12 | 赵六 | 2017-01-01 00:00:00 | 女 | | 13 | 孙七 | 2018-01-01 00:00:00 | 女 | +------+-------+---------------------+------+ */
固然这里碰到了一个小问题,把查询语句的WHERE条件改为sc.SId=null时,取出的是空集:
select stu.* from student as stu left join sc on stu.SId=sc.SId where sc.SId=null; /* Empty set (0.08 sec) */
在WHERE子句中,column = null永远不会为true,以这种方式使用null无效,要检测值为NULL的列,必须使用IS NULL或列IS NOT NULL。关于NULL的使用有专门的章节:Working with NULL Values。
2.5
USING(join_column_list)子句指定两个表中必须拥有的列的列表。若是表a和b都包含列c1,c2和c3,则如下链接将比较两个表中的相应列:
a LEFT JOIN b USING (c1, c2, c3)
2.6
两个表的NATURAL [LEFT] JOIN等下于下面的状况:带有USING子句的INNER JOIN或LEFT JOIN,该子句列出了在两个表中都存在的全部的列。
2.7
RIGHT JOIN的工做方式相似于LEFT JOIN。为了使代码能够跨数据库移植,建议您使用LEFT JOIN而不是RIGHT JOIN。
2.8
语法描述中的{ OJ...},只是为了兼容ODBC。这个花括号必须按字面编写。
SELECT left_tbl.* FROM { OJ left_tbl LEFT OUTER JOIN right_tbl ON left_tbl.id = right_tbl.id } WHERE right_tbl.id IS NULL;
您能够在{OJ ...}中使用其余类型的链接,例如INNER JOIN或RIGHT OUTER JOIN。这有助于与某些第三方应用程序兼容,但不是官方ODBC语法。
2.9
STRAIGHT_JOIN相似于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的JOIN,包括外链接的变体,是根据SQL-2003的标准进行处理的。
2.10
NATURAL链接中的冗余列不会显示。
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);
第一个和第二个SELECT语句中的“j”列,都只会出现一次:
/* +------+------+------+ | j | i | k | +------+------+------+ | 1 | 1 | 1 | +------+------+------+ +------+------+------+ | j | i | k | +------+------+------+ | 1 | 1 | 1 | +------+------+------+ */
冗余列的消除和列的排序都是根据标准SQL进行处理,按下面的顺序展现:
取代两个表的相同列的单列是经过使用coalesce(合并)操做来定义的,也就是说,对于两个t1.a和t2.a,获得的单个链接列a被定义为a = COALESCE(t1.a,t2.a):
COALESCE(x, y) = (CASE WHEN x IS NOT NULL THEN x ELSE y END)
若是任何其余的join操做,则链接的结果列由参与链接的表的全部列的串联组成。合并的列的定义结果是,对于外链接,若是两列中的一列始终为NULL,则合并列包含非NULL列的值。若是两列都不为NULL或者都为NULL,两个公共列具备相同的值,所以选择哪一列做为合并列的值就可有可无了。解释这一点的一种简单方法是考虑外链接的合并列由JOIN的内部表的公共列表示。
假设表t1(a,b)和t2(a,c)具备如下内容:
/* t1 t2 ---- ---- 1 x 2 z 2 y 3 w */
那么下面这个JOIN,列a包含的是t1.a的值:
SELECT * FROM t1 NATURAL LEFT JOIN t2; +------+------+------+ | a | b | c | +------+------+------+ | 1 | x | NULL | | 2 | y | z | +------+------+------+
而下面的JOIN,刚好相反,a列包含的是t2.a的值:
SELECT * FROM t1 NATURAL RIGHT JOIN t2;
+------+------+------+ | a | c | b | +------+------+------+ | 2 | z | y | | 3 | w | NULL | +------+------+------+
将这些结果与JOIN ... ON的等效查询进行比较:
SELECT * FROM t1 LEFT JOIN t2 ON (t1.a = t2.a); +------+------+------+------+ | a | b | a | c | +------+------+------+------+ | 1 | x | NULL | NULL | | 2 | y | 2 | z | +------+------+------+------+
SELECT * FROM t1 RIGHT JOIN t2 ON (t1.a = t2.a); +------+------+------+------+ | a | b | a | c | +------+------+------+------+ | 2 | y | 2 | z | | NULL | NULL | 3 | w | +------+------+------+------+
2.11
USING子句可使用ON子句进行重写。尽管他们两个很像,但仍是有所不一样。
看下下面两个查询:
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
在筛选条件上,这两个链接在语义上是一致的。可是在“要为SELECT *扩展显示哪些列”上,这两个链接在语义上并不相同。USING链接选择相应列的合并值,而ON链接选择全部表中的全部列。
对使用USING的JOIN,SELECT *选择这些值:
COALESCE(a.c1, b.c1), COALESCE(a.c2, b.c2), COALESCE(a.c3, b.c3)
而使用ON的JOIN,SELECT *选择以下:
a.c1, a.c2, a.c3, b.c1, b.c2, b.c3
对于内链接,COALESCE(a.c1,b.c1)与a.c1或b.c1相同,由于两列的值都相同。
对于外链接(例如LEFT JOIN),两列中的一列能够为NULL。该列会从结果中略去。
2.12
ON子句只能引用其操做范围内的操做数。
CREATE TABLE t1 (i1 INT); CREATE TABLE t2 (i2 INT); CREATE TABLE t3 (i3 INT); SELECT * FROM t1 JOIN t2 ON (i1 = i3) JOIN t3;
执这个SELECT语句会报错:Unknown column 'i3' in 'on clause' ,由于i3是t3中的一列,它不是ON子句的操做数。
对此语句进行修改:
SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i3);
对ON的做用范围进行测试,如下语句均能执行:
SELECT * FROM t1 JOIN t2 JOIN t3 ON (i2 = i3); Empty set (0.00 sec) SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i2); Empty set (0.00 sec)
即ON对其以前的JOIN中的表的列都能引用。
2.13
JOIN比逗号操做符拥有更高的优先级,因此下面这个表达式:
t1, t2 JOIN t3
会被解释为:
(t1, (t2 JOIN t3))
而不是:
((t1, t2) JOIN t3)
这个特色会影响使用ON子句的语句,由于ON子句只能引用JOIN操做的表中的列,优先级会影响对这些操做表的解释。执行以下的语句就报错了:
SELECT * FROM t1,t2 JOIN t3 ON (i1 = i2); ERROR 1054 (42S22): Unknown column 'i1' in 'on clause'
而这样就能成功执行:
SELECT * FROM (t1,t2) JOIN t3 ON (i1 = i2); Empty set (0.00 sec)
或者不适用逗号:
SELECT * FROM t1 join t2 JOIN t3 ON (i1 = i2); Empty set (0.00 sec)
此外,INNER JOIN,CROSS JOIN,LEFT JOIN和RIGHT JOIN混合的语句中,全部这些语句的优先级都高于逗号运算符。
2.14
与SQL:2003标准相比,MySQL扩展是MySQL容许您限定NATURAL或USING链接的公共(coalesced合并)列,而标准SQL不容许这样作。