在数据库设计中,基本上都是根实体ER关系和范式设计多张表。这样更能设计出更合理、紧凑的表结构,提高有效数据存储,数据的管理维护更简便,也提高了库表的伸缩性。
但是事物总是平衡的,在很多场景中单张表的数据不能满足需求,需要根据多张关联表的数据逻辑关系,返回关联数据。如:查询选修了英语课程的所有学生信息。这里就涉及到学生和课程实体的之间的关联关系。这种查询必然需要操作两张表:
- 可以分步骤多次查询
- 也可以使用前面提到的子查询
但是这两种方式性能不如:联结。这也是多张表的带来的弊端:SQL的复杂化,DBMS需要更强的功能支持。
join是将多张表混合在一起根据数据之间的逻辑关系进行查询操作。联结的类型很多:
- 自联结
- 自然联结(等值联结或者内连接)
- 外链接
自联结:同一张表使用别名和自己进行连接查询
自然连接:多张表使用逻辑关联列进行进行联结查询,只查询出关联列在多张表完全相等的数据,多张表中对于的未关联上的数据不在查询结果中。
外联结:针对上述自然连接,如果需要将未关联上的行也需要查询,即是外联结。外联结根据查询出不同表的未关联的数据,又分为:左联结、右联结、全联结。
下面看个例子:
前提:
create table student (stu_id int, stu_name varchar(10), stu_age smallint);
create table course(course_id int, course_name varchar(10));
create table score(stu_id int, course_id int, score float);
insert into student(stu_id, stu_name, stu_age) values (1, 'xx', 15),(2, 'dd', 17),(3, 'zz', 18);
insert into student(stu_id, stu_name, stu_age) values (4, 'gg', 15);
insert into course(course_id, course_name) values (1, 'shuxue'),(2, 'yuwen'),(3, 'yingyu'),(4, 'wuli'),(5, 'huaxue');
insert into score(stu_id, course_id, score) values (1, 1, 87.5),(1, 2, 79.7),(1, 3, 90.3),(1, 4, 89.1),(1, 5, 91.4);
insert into score(stu_id, course_id, score) values (2, 1, 71.4),(2, 2, 56.3),(2, 3, 78.4),(2, 4, 89.1),(2, 5, 93.2);
insert into score(stu_id, course_id, score) values (3, 1, 59.4),(3, 2, 40.3),(3, 3, 25.4),(3, 4, 100),(3, 5, 98.9);
insert into score(stu_id, course_id, score) values (4, 1, 16.0),(4, 2, 20.5),(4, 3, 19.4);
-- 查询英语成绩在90分及以上的学生
select stu.* from student as stu, score as sco where stu.stu_id = sco.stu_id and sco.course_id = 3 and sco.score >= 90;
-- 查询每个学生的所有课程的分数
select tmp.stu_name, tmp.score, cos.course_name from (select stu.stu_name, sco.score, sco.course_id from student as stu left outer join score as sco on stu.stu_id = sco.stu_id) as tmp left outer join course as cos on tmp.course_id = cos.course_id order by tmp.stu_name, tmp.score;
自联结和内联结时,条件子句一般使用where,在外联结中多数使用on子句。
内联结:可以直接在from子句后用逗号分割表名,亦可以使用inner join关键字联结表名
左联结:可以使用left join或者left outer join关键字联结表名
右联结:可以使用right join或者rigth outer join关键字联结表名
全连接:可以使用full join或者full outer join关键字联结表名
下面再来看下区别几种外联结的区别:
-- 左联结
select stu.*, sco.course_id, sco.score from student as stu left join score as sco on stu.stu_id = sco.stu_id;
结果:
1 | xx | 15 | 1 | 87.5 |
1 | xx | 15 | 2 | 79.7 |
1 | xx | 15 | 3 | 90.3 |
1 | xx | 15 | 4 | 89.1 |
1 | xx | 15 | 5 | 91.4 |
2 | dd | 17 | 1 | 71.4 |
2 | dd | 17 | 2 | 56.3 |
2 | dd | 17 | 3 | 78.4 |
2 | dd | 17 | 4 | 89.1 |
2 | dd | 17 | 5 | 93.2 |
3 | zz | 18 | 1 | 59.4 |
3 | zz | 18 | 2 | 40.3 |
3 | zz | 18 | 3 | 25.4 |
3 | zz | 18 | 4 | 100 |
3 | zz | 18 | 5 | 98.9 |
4 | gg | 15 | 1 | 16 |
4 | gg | 15 | 2 | 20.5 |
4 | gg | 15 | 3 | 19.4 |
5 | mm | 28 | NULL | NULL |
可以看出最后一行中关于分数的部分全是null,这就是左联结的特点,如果左边表的数据在右边无法连接,则全部补null。
右联结同理,这里就不再赘述。
参考
《SQL必知必会》