翻译完成有一种莫名的幸福感---
JOIN 语法
Mysql支持select 语句和多表delete、update 语句的表引用部分的下述 JOIN 语法。
表引用也称为连接表达式。
表引用可能包含一个 PARTITION 选项,包括一系列的逗号分隔区间、子区间,或者两者皆有。这个选项遵循表命名,位于任何别名声明之前。此选项的作用是选择的行只能来自于列出的区间或者子区间。任何未列举出来的区间或子区间都被忽略。想了解更多信息和例子,看章节23.5,“Partition Selection”
与标准的 SQL 相比,table_factor 的语法是 MySQL的扩展。标准 SQL 只接受 table_reference,不接受一对圆括号内的他们的列表。
如果表引用元素中的每个逗号都被当做等价于 inner join,那这是一个保守的扩展。如:
select * from t1 left join (t2,t3,t4) on (t2.a=t1.a and t3.b=t1.b and t4.c=t1.c)
等价于
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 子句一起使用,否则使用交叉连接。
总的来说,在连接表达式中包含 inner join操作时,括号可以被忽略。MySQL 也支持嵌套的连接。参照章节8.2.1.7,”Nested Join Optimization”。
索引提示可以具体影响MySQL优化器如何使用索引。更多的信息,参照章节8.9.4,“Index Hints”.优化器提示和 optimizer_switch 系统变量是影响优化器使用索引的方式,参照章节8.9.2,“Optimizer Hints”和章节8.9.3,“Switchable Optimizations”.
下面列举一些当写 joins 语句时需要纳入考虑的因素:
- 表引用可以使用别名,通过tb1_name as alias_name或 tb1_name alias_name 语法表示。
- table_subquery 也可以认为是位于 from 语句后的衍生表或子查询。参照章节13.2.11.8,“Derived Tables”。像这样的子查询必须添加别名,以便于为子查询结果提供一个表名,并可以选择性的在括号内包含表的列名。下面是一个小例子:
select * from (select 1,2,3) as t1;
- inner join和逗号在没有 join 条件的情况下语义是一样的:两者都在指定的表之间产生笛卡尔积(就是,第一张表的每一行与第二张表的每一行连接)。
可是,逗号操作符的优先级比 inner join,cross join,left join等低。如果你将逗号连接与其他类型连接混合,会产生on语句后未知列“col_name”的错误。处理这个问题的方法将在本章节后面阐述。 - 通过 on 使用的condition_expr可以是任何可以在 where 子句中使用的条件表达式。总的来说,on子句服务于具体指定如何连接表的条件,而 where 子句限制哪些行包括在结果集中。
- 在left join中使用 on 或 using 时,如果右表无任何匹配的行,那么右表所有的行都会设为 null。你可以使用这个用法来查找某个表在其他表无副本的行。
select left_tb1.* from left_tb1 left join right_tb1 on left_tb1.id=right_tb1.id where right_tb1.id is null;
这个例子查找 left_tb1的 id 值并没有出现在 right_tb1的所有行(也就是,left_tb1与 right_tb1无关的所有行)。参照章节8.2.1.8,“Outer join Optimization”。
- using(column_list)子句表示必须同时出现在两张表中的一些列名。如果表 a 和表 b都包含列 c1,c2和 c3,下述连接比较两张表相关联的列。
a left join b using(c1,c2,c3)
- natural [left] join连接两张表定义为语义上等同于 inner join 或者 具有 use 子句的left join,use 子句命名两个表中都存在的列。
- Right join作用与 left join 相似。为了让代码可以便携跨越数据库,推荐使用 left join 代替 right join。
- 出现在 join 语法描述中的{OJ…}语法只在 ODBC 中兼容。语法中的大括号应该按照字面意思写;他们不是在各个地方都能使用的语法描述的元语法。
你可以在{OJ …}中使用其他类型的连接,如 inner join,right join,right outer join。这有助于兼容不是官方的ODBC(Open Database Connectivity,开放数据库互联,提供一种标准的 API 方法来访问数据库管理系统)语法的第三方应用。 - straight_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;
自然连接和使用的 using 的连接,包括外连接的变体,处理都遵照 SQL:2003标准。
- 在自然连接中多余的列不会出现。思考下述语句:
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;
在第一个 select 语句中,列 j 出现在两张表中所以成为了 join 的列,所以,根据标准 SQL,它只会在结果集中出现一次,而不是两次。同样的,在第二个 select 语句中, j列在 using 子句中命名,也只会在结果集中出现一次。
冗余的列消除和列顺序的出现遵循 SQL 规范,产生了这样的展示顺序。
A. 首先,两张连接表的合并列的顺序,由第一张表的出现顺序决定。
B. 第二,第一张表独有的列,以第一张表出现的顺序决定。
C.第三,第二张表独有的列,以第二章表出现的顺序决定。
代替两个公共列的单个结果列被定义为合并操作。就是,对于两列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)
如果连接操作是任何其他连接,连接的结果列由连接表的所有列串联组成。
由定义的和并列引起的后果是,对于外连接,如果两者之一的列总是 null,合并的列包含非空的列。如果两个都不为空,两个公共列有相同的值,所以无需选择哪个值作为合并列的值。一个简单的方式解释是考虑外连接的合并列代表内部表连接的公共列。假设表t1(a,b)和t2(a,c)有如下内容:
t1 t2
1 x 2 z
2 y 3 w
对于这个连接,列 a 包含t1.a的值:
mysql> SELECT * FROM t1 NATURAL LEFT JOIN t2;
+------+------+------+
| a | b | c |
+------+------+------+
| 1 | x | NULL |
| 2 | y | z |
+------+------+------+
作为对比,这个连接,列 a 包含 t2.a的值:
mysql> SELECT * FROM t1 NATURAL RIGHT JOIN t2;
+------+------+------+
| a | c | b |
+------+------+------+
| 2 | z | y |
| 3 | w | NULL |
+------+------+------+
将这些结果与其他使用 join … on 的等价查询比较
mysql> SELECT * FROM t1 LEFT JOIN t2 ON (t1.a = t2.a);
+------+------+------+------+
| a | b | a | c |
+------+------+------+------+
| 1 | x | NULL | NULL |
| 2 | y | 2 | z |
+------+------+------+------+
mysql> SELECT * FROM t1 RIGHT JOIN t2 ON (t1.a = t2.a);
+------+------+------+------+
| a | b | a | c |
+------+------+------+------+
| 2 | y | 2 | z |
| NULL | NULL | 3 | w |
+------+------+------+------+
- using子句可以被重写成比较关联列的 on 子句。即使 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
关于决定哪行满足 join 条件,两个 join 语义上是相同的。
关于决定哪些列在 select *扩展中展示,这两个连接语义上不相同。using 连接选择合并相关列的值,而 on 连接选择所有表的所有列。对于 using 连接,select * 选择了这些值:
COALESCE(a.c1, b.c1), COALESCE(a.c2, b.c2), COALESCE(a.c3, b.c3)
对于 on 连接,select * 选择了这些值:
a.c1, a.c2, a.c3, b.c1, b.c2, b.c3
对于一个内部连接,coalesce(a.c1,b.c1)和 a.c1,b.c1两者之一都相同,因为两者的列有相同的值。对于外连接(如 left join),两者之一的列会为空。列从结果中省略。
- 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;
语句因 on 语句中未知的列 i3失败报错,因为 i3是 t3的列,它不是 on 子句的操作数。为了使连接可运行,如下重写语句:
SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i3);
- Join比逗号有更高的优先级,所以 连接表达式 t1,t2 join t3 可翻译成(t1,(t2 join t3)),而不是((t1,t2) join t3)。这影响使用 on 子句的语句,因为子句只能引用连接的操作数的列,并且优先级影响那些操作数是是什么的解释。
如:
CREATE TABLE t1 (i1 INT, j1 INT);
CREATE TABLE t2 (i2 INT, j2 INT);
CREATE TABLE t3 (i3 INT, j3 INT);
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t2 VALUES(1, 1);
INSERT INTO t3 VALUES(1, 1);
SELECT * FROM t1, t2 JOIN t3 ON (t1.i1 = t3.i3);
join 优先于逗号运算符,所以 on 子句的操作数是 t2和 t3。因为 t1.i1不是两者任一操作数的列,结果就是错误: on 子句后未知列 t1.i1。
为了使连接可以运行,使用这些策略的任意一个:
A. 显示使用括号将两张表化为一组,以便 on 子句的操作数为(t1,t2)和 t3:
SELECT * FROM (t1, t2) JOIN t3 ON (t1.i1 = t3.i3);
B. 避免使用逗号运算符,使用 join 代替:
SELECT * FROM t1 JOIN t2 JOIN t3 ON (t1.i1 = t3.i3);
相同的优先解释也适用于混合逗号运算符和 inner join,cross join,left join和 right join的语句,这些子句优先于逗号操作符。
- 相比于 SQL:2003标准的 MySQL 的拓展是,MySQL 允许你有资格使用 natural 或者 using 连接的和并列,而标准的不允许。
遗留问题:
1. join默认执行的是什么连接?
2.内连接,外连接?
3.嵌套 join?
4.table reference?
//TODO
总结一番:
网友评论