外连接
虽然MySQL不支持SQL 92中的左外连接、右外连接,但本书还是有必要了解一下SQL 92中的左外连接和右外连接。SQL 92中的外连接就是在连接条件的列名后增加括号包起来的外连接符(+或*, 不同的数据库有一定的区别),当外连接符出现在左边时称为左外连接,出现在右边时则称为右外连接。SQL语句如下:
select s.*, teacher name from student_table s, teacher_table t #右外连接 where s.java_teacher = t.teacher_id(*);
外连接就是在外连接符所在的表中增加一个“万能行”,这行记录的所有数据都是null,而且该行可以与另一个表中所有不满足条件的记录进行匹配,通过这种方式就可以把另一个表中的所有记录选来,不管这些记录是否满足连接条件。
自连接
除此之外,还有一种自连接,正如前面介绍外键约束时提到的自关联,如果同一个表中的不同记录之间存在主、外键的束关联,例如把员工、经理保存在同一个表里,则需要使用自连接查询。
注意:
自连接只是连接的一种用法、并不是一种连接类型、不管是SQL 92还是SQL 99都可 以使用自连接查询、自连接的本质就是把一个表当成两个表来用。
下面的SQL句建立了一个自关联的数据表,并向表中插入了4条数据。
create table emp_table ( emp_id int auto_ineroment primary key, enp_nane varchar(255), manager_id int, foreign key(manager_id) referenees emp_table(emp_id) ); insert into emp_table values (null, '唐僧',null), (null,'孙悟空',1), (null,'猪八戒',1), (null,'沙憎',1);
如果需要查询该数据表中的所有员工名,以及每个员工对应的经理名,则必须使用自连接查询。所谓自连接就是把一个表当成两个表来用,这就需要为一个表起两个别名,而且查询中用的所有数据列都要加表别名前缀,因为两个表的数据列完全一样。下面的自连接查询可以查询出所有的员工名,以及对应的经理名。
select emp.emp_id, emp.emp_name员工名,mgr.emp_name经理名 from emp table_emp, emp_table mqr where emp.manager_id = mgr.emp_id;
2. SQL 99的连接查询
交叉连接(cross join)
SQL语句如下:
select s.*, teacher_name #SQL 99 多表连接查询的from后只有一个表名 from student_table s #cross join交叉连接,相当于广义笛卡儿积 cross join teacher_table t;
自然连接(natural join)
SQL语句如下:
select s.*, teacher_name #SQL 99 多表连接查询的from后只有一个表 from student_table s #natural join自然连接使用两个表中的同名列作为连接条件 natural join teacher _table t;
using子句连接
SQL语句如下:
elect s.*, teacher_name #SQL 99 事表连接查询的from后只有一个表名 from student table s # join连接另一个表 join teacher_table t using (teacher id);
运行上面语句将出现一个错误,因为student_table表中并不存在名为tcacher_id的列。也就是说,如果使用using子句来指定连接条件,则两个表中必须有同名列,否则就会出现错误。
on子句连接
SQL语句如下:
select s.*, teacher_name # SQL 99 多表连接查询的from后只有一个表名 from student_table s # join连接另一个表 join teacher_table t # 使用on来指定连接条件 on s.java_teacher = t.teacher_id;
使用on子句的连接完全可以代替SQL 92中的等值连接、非等值连接,因为on子句的连接条件除等值条件之外,也可以是非等值条件。如下SQL语句就是SQL 99中的非等值连接。
select s.* ,teacher_name # SQL 99 多表连接查询的from后只有一个表名 from student_table s * join 连接另一个表 join teacher_tabie t # 使用on来指定连接条件:非等值连接 on s.java_teacher > t.teacher_id;
左、右、全外连接
下面使用右外连接,连接条件是非等值连接。
select s.* , teacher_name # SQL99多表连接查询的from后只有一个表名 from student_table s # right join右外连接另一个表 right join teacher_table t # 使用on来指定连接条件,使用非等值连接 on s.java_teacher < t.teacher_id;
下面使用左外连接,连接条件是非等值连接。
select s.*, teacher name # SQL 99 多表连接查询的from后只有一个表名 from student_table s # left join左外连接另一个表 left join teacher_table t # 使用on来指定连接条件,使用非等值连接 on s.java_teacher > t.teacher_id;
下面的SQL语句使用全外连接,连接条件是等值连接。
select s.*, teacher_name # SQL 99 多表连接查询的from后只有一个表名 from student_table s # full join全外连接另一个表 full join teacher_table t # 使用on来指定连接条件,使用等值连接 on s.java.teacher = t.teacher_id;
SQL 99 的全外连接将会把两个表中所有不满足连接条件的记录全部列出。
注意:
运行上面查询语句时会出现错误,这是因为 MySQL 并不支持全外连接
13 子查询
下面的SQL语句示范了把子查询当成数据表的用法。
select * # 把子查询当成数据表 from (select * from student_table) t where t.java_teacher > 1;
还有一种情形:把子查询当成where条件中的值,如果子查询返回单行、单列值,则被当成一个标量值使用,也就可以使用单行记录比较运算符。例如如下SQL语句:
select * from student_table where java_teacher > # 返回单行、单列的子查询可以当成标量值使用 (select teacher_id from teacher_table where teacher_name='Yeeku');
上面查询语句中的子查询(粗体字部分)将返回一个单行、单列值(该值就是1),如果把上面查询语句的括号部分换成1,那么这条语句就再简单不过了一一实际上,这就是这种子查询的实质,单行、单列子查询的返回值被当成标量值处理。
如果子查询返回多个值,则需要使用in、any 和all等关键字,in可以单独使用,与前面介绍比较.运算符时所讲的in完全一样,此时可以把子查询返回的多个值当成一个值列表。SQL语句如下:
select * from student_table where student_id in (select teacher_id from teacher_table);
上面查询语合中的子查询(粗体字部分)将返回多个值,这多个值将被当成一个值列表,只要student id与该值列表中的任意一个值相等,就可以选出这条记录。
any和all可以与>、>=、<、<=、<>、=等运算符结合使用,与any结合使用分别表示大于、大于等于、小于,小于等于、不等于、等于其中任意一个值;与all结合使用分别表示大于、大于等于、小于,小于等于、不等于、 等于全部值。从上面介绍中可以看出,=any的作用与in的作用相同。如下SQL 语句使用=any来代替上面的in.
select * from student_table where student_id = any(select teacher_id from teacher_table);
<ANY 只要小于值列表中的最大值即可,>ANY要求大于值列表中的最小值。<All要求小于值列表中的最小值,>ANY要求大于值列表中的最大值。
下面的SOL语句选出student_table表中student_id 大于teacher_table表中所有teacher id的记录.
select * from student_table where student id > all(select teacher_id from teacher_table);
还有一种子查询可以返回多行、多列,此时where子句中应该有对应的数据列,并使用圆括号将多个数据列组合起来。SQL语句如下
select * from student_table where (student_id, student_name) = any(select teacher_id, teacher_name from teacher_table);
14 集合运算
1. union 运算
union运算的语法格式如下:
select 语句 union select 语句
下面的SQL语句查询出所有教师的信息和主键小于4的学生信息。
# 查询结果集包含两列,第一列为int类型,第二列为varchar类型 select * from teacher_table union # 这个结果集的数据列必须与前一个结果集的数据列一一对应 select student_id, student_name from student_table;
2. minus 运算
minus运算的语法格式如下:
select 语句 minus select 语句
上面的语法格式十分简单,不过很遗憾,MySQL并不支持使用minus运算符,因此只能借助子查询来“曲线”实现上面的minus运算。
假如想从所有学生记录中“减去”与老师记录的ID相同、姓名相同的记录、则可进行如下的minus运算:
select student_id, student_name from student_table minus # 两个结果集的数据列的数量相等,数据类型一一对应,可以进行minus运算 select teacher_id, teacher_name from teacher_table;
不过,MySQL并不支持这种运算。但可以通过如下子查询来实现上面运算。
select student_id, student_name from student_table where (student_id, student_name not in (select teacher_id, teacher_name from teacher_table)
3. intersect 运算
intersect 运算的语法格式如下:
select 语句 intersect select 语句
上面的语法格式十分简单,不过很遗憾,MySQL并不支持使用 intersect 运算符,因此只能借助于多表连接查询来“曲线”实现上面的 intersect 运算。
假如想找出学生记录中与老师记录中的D相同、姓名相同的记录,则可进行如下的intersect运算:
select student_id, student_name from student_table intersect #两个结果集的数据列的数量相等,数据类型一一对应,可以进行intersect运算 select teacher_id, teacher_name from teacher_table;
不过,MySOL并不支持这种运算。但可以通过如下多表连接查询来实现上面运算
select student_id, student_name from student_table join teacher_table on(student_id = teacher_id and student_name = teacher_name);
需要指出的是,如果进行intersect运算的两个select子句中都包括了where条件,那么将intersect 运算改写成多表连接查询后还需要将两个where条件进行and运算。假如有如下 intersect 运算的SQL 语句:
select student_id, student_name from student_table where student_id < 4 intersect # 两个结果集的数据列的数量相等,数据类型一一对应,可以进行intersect运算 select teacher_id, teacher_name from teacher_table where teacher_name like '李%';
上面语句改写如下:
select student_id, student_name from student_table join teacher_table on (student_id = teacher_id and student_name = teacher_name) where student_id < 4 and teacher_name like '李%';