1.背景
没什么背景,就是想记录下。
2.本文使用的表结构以及数据
本文使用的数据库为MySQL 5.7,下面是使用到的数据,数据量不多,但是能测出一点区别。
--- 学生表
create table student (
sno varchar(20) comment '学号',
sname varchar(20) comment '姓名',
sd varchar(10) comment '系',
sgae int comment '年龄',
sex int comment '性别',
addr varchar(50) comment '家庭住址',
primary key(sno)
);
--- 课程表
create table course (
cno varchar(20) comment '课程号',
cname varchar(20) comment '课程名',
pcno varchar(50) comment '先修课程号',
primary key(cno)
);
--- 学生选课
create table sc(
sno varchar(20) comment '学号',
cno varchar(20) comment '课程号',
grade decimal(4,2) comment '成绩',
primary key(sno,cno),
foreign key(sno) references student(sno),
foreign key(cno) references course(cno)
);
--- 插入数据
insert into student values('s1000001','张三','工学部',18,1,'北京'),('s1000002','张四','工学部',18,1,'上海'),('s1000003','张五','工学部',19,1,'广州'),('s1000004','张六','工学部',17,1,'深圳'),('s1000005','张七','工学部',18,1,'北京'),('s1000006','张八','工学部',19,1,'上海'),('s1000007','张九','工学部',20,1,'广州'),('s1000008','张十','工学部',18,1,'深圳'),('s1000009','李三','工学部',17,1,'北京'),('s1000010','李四','工学部',16,1,'上海'),('s1000011','李五','工学部',19,1,'广州');
insert into course values('c1001','操作系统',null),('c1002','计算机组成原理',null),('c1003','数据结构',null),('c1004','计算机网络',null),('c1005','算法',null),('c1006','软件工程概率',null),('c1007','离散数学','c1008'),('c1008','高等数学',null);
insert into sc values('s1000001','c1001',3.8),('s1000001','c1002',4.3),('s1000001','c1003',4.6),('s1000001','c1004',4.0),('s1000001','c1005',4.9),('s1000001','c1006',4.9),('s1000001','c1007',4.9),('s1000001','c1008',2.7),('s1000002','c1001',3.6),('s1000002','c1002',4.9),('s1000002','c1003',4.9),('s1000002','c1004',4.9),('s1000002','c1005',4.9),('s1000002','c1006',4.9),('s1000002','c1007',3.1),('s1000002','c1008',4.9),('s1000003','c1001',3.9),('s1000003','c1002',3.5),('s1000003','c1003',3.2),('s1000003','c1004',2.4),('s1000003','c1005',4.9),('s1000003','c1006',4.9),('s1000003','c1007',3.1),('s1000003','c1008',3.3),('s1000004','c1001',3.2),('s1000004','c1002',4.9),('s1000004','c1003',3.3),('s1000004','c1004',2.8),('s1000004','c1005',4.9),('s1000004','c1006',3.2),('s1000004','c1007',4.9),('s1000004','c1008',3.9),('s1000005','c1001',4.2),('s1000005','c1002',4.9),('s1000005','c1003',4.9),('s1000005','c1004',2.7),('s1000005','c1005',3.9),('s1000005','c1006',2.1),('s1000005','c1007',4.9),('s1000005','c1008',4.9),('s1000006','c1001',4.9),('s1000006','c1002',4.9),('s1000006','c1003',3.5),('s1000006','c1004',3.4),('s1000006','c1005',4.9),('s1000006','c1006',3.4),('s1000006','c1007',3.2),('s1000006','c1008',4.9),('s1000007','c1001',3.2),('s1000007','c1002',3.4),('s1000007','c1003',1.9),('s1000007','c1004',3.0),('s1000007','c1005',3.6),('s1000007','c1006',4.9),('s1000007','c1007',3.5),('s1000007','c1008',4.9),('s1000008','c1001',3.4),('s1000008','c1002',4.9),('s1000008','c1003',3.2),('s1000008','c1004',2.6),('s1000008','c1005',4.9),('s1000008','c1006',3.9),('s1000008','c1007',3.9),('s1000008','c1008',4.9),('s1000009','c1001',3.7),('s1000009','c1002',2.5),('s1000009','c1003',3.1),('s1000009','c1004',4.9),('s1000009','c1005',2.9),('s1000009','c1006',3.1),('s1000009','c1007',4.9),('s1000009','c1008',1.9),('s1000010','c1001',3.5),('s1000010','c1002',4.6),('s1000010','c1003',3.9),('s1000010','c1004',3.9),('s1000010','c1005',4.9),('s1000010','c1006',3.3),('s1000010','c1007',3.6),('s1000010','c1008',4.9)
3.not in执行效果
使用not in查看没有选修任何课程的学生,耗时0.01s:
select s.* from student s where s.sno not in (select a.sno from sc a);
+----------+--------+-----------+------+------+--------+
| sno | sname | sd | sgae | sex | addr |
+----------+--------+-----------+------+------+--------+
| s1000011 | 李五 | 工学部 | 19 | 1 | 广州 |
+----------+--------+-----------+------+------+--------+
1 row in set (0.01 sec)
4.not exists执行效果
使用not exists查看没有选修任何课程的学生,耗时0.03s:
select s.* from student s where not exists (select 1 from sc a where a.sno=s.sno);
+----------+--------+-----------+------+------+--------+
| sno | sname | sd | sgae | sex | addr |
+----------+--------+-----------+------+------+--------+
| s1000011 | 李五 | 工学部 | 19 | 1 | 广州 |
+----------+--------+-----------+------+------+--------+
1 row in set (0.03 sec)
5.优化
上面的not in和not exists是等价的,但是not exists明显比not in慢。在数据量很大的情况下,是不推荐使用这两个的,这种情况下,我们可以将sql改为left join的方式。
5.1.left join
left join顾名思义为左连接,语法为:
select a.* from a left join b on a.id = b.id [where ……]
left join语法中on和where的执行顺序为:先执行on生成临时表,再执行where对临时表做过滤。
on 条件是在生成临时表时使用的条件,它不管 on 中的条件是否为真,都会返回左边表中的记录,以下面的两张表为例,用a表作为左表,b表作为右表,执行left join,返回4条记录(注意不是3条!)。
mysql> select * from tb1 a;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
mysql> select * from tb2 b;
+------+
| id |
+------+
| 1 |
| 2 |
| 4 |
| 2 |
+------+
4 rows in set (0.00 sec)
mysql> select a.* from tb1 a left join tb2 b on a.id=b.id;
+------+
| id |
+------+
| 1 |
| 2 |
| 2 |
| 3 |
+------+
4 rows in set (0.00 sec)
NOTE:
对于left join,部分同学误解为返回的记录数一定是等于左表的记录数,其实不是的。
- 在没有where的条件下,执行on时,以左表为基准,去右表匹配,如从a表拿id=1去b表匹配,b表有3条id=1的记录,此时就会返回3条记录。
- 在有where的条件下,执行完on后得到的结果需要经过where条件去过滤。
综上所述,left join的结果集不一定等于左表。
5.2.not in和not exists改造为left join
改为left join的语法为,耗时0.00s:
select s.* from student s left join sc a on s.sno=a.sno where a.sno is null;
+----------+--------+-----------+------+------+--------+
| sno | sname | sd | sgae | sex | addr |
+----------+--------+-----------+------+------+--------+
| s1000011 | 李五 | 工学部 | 19 | 1 | 广州 |
+----------+--------+-----------+------+------+--------+
1 row in set (0.00 sec)
前面提到,先执行on生成临时表,因为student是左表,所以执行on时,左表的11条记录中有10条记录每条能匹配到8条课程分数记录,而sno为s1000011的记录没有匹配到数据,所以右边补上NULL。总共得到的数据为81条:
mysql> select s.*,a.* from student s left join sc a on s.sno=a.sno;
+----------+--------+-----------+------+------+--------+----------+-------+-------+
| sno | sname | sd | sgae | sex | addr | sno | cno | grade |
+----------+--------+-----------+------+------+--------+----------+-------+-------+
| s1000001 | 张三 | 工学部 | 18 | 1 | 北京 | s1000001 | c1001 | 3.80 |
| s1000001 | 张三 | 工学部 | 18 | 1 | 北京 | s1000001 | c1002 | 4.30 |
| s1000001 | 张三 | 工学部 | 18 | 1 | 北京 | s1000001 | c1003 | 4.60 |
| s1000001 | 张三 | 工学部 | 18 | 1 | 北京 | s1000001 | c1004 | 4.00 |
| s1000001 | 张三 | 工学部 | 18 | 1 | 北京 | s1000001 | c1005 | 4.90 |
| s1000001 | 张三 | 工学部 | 18 | 1 | 北京 | s1000001 | c1006 | 4.90 |
| s1000001 | 张三 | 工学部 | 18 | 1 | 北京 | s1000001 | c1007 | 4.90 |
| s1000001 | 张三 | 工学部 | 18 | 1 | 北京 | s1000001 | c1008 | 2.70 |
| s1000002 | 张四 | 工学部 | 18 | 1 | 上海 | s1000002 | c1001 | 3.60 |
| s1000002 | 张四 | 工学部 | 18 | 1 | 上海 | s1000002 | c1002 | 4.90 |
| s1000002 | 张四 | 工学部 | 18 | 1 | 上海 | s1000002 | c1003 | 4.90 |
| s1000002 | 张四 | 工学部 | 18 | 1 | 上海 | s1000002 | c1004 | 4.90 |
| s1000002 | 张四 | 工学部 | 18 | 1 | 上海 | s1000002 | c1005 | 4.90 |
| s1000002 | 张四 | 工学部 | 18 | 1 | 上海 | s1000002 | c1006 | 4.90 |
| s1000002 | 张四 | 工学部 | 18 | 1 | 上海 | s1000002 | c1007 | 3.10 |
| s1000002 | 张四 | 工学部 | 18 | 1 | 上海 | s1000002 | c1008 | 4.90 |
| s1000003 | 张五 | 工学部 | 19 | 1 | 广州 | s1000003 | c1001 | 3.90 |
| s1000003 | 张五 | 工学部 | 19 | 1 | 广州 | s1000003 | c1002 | 3.50 |
| s1000003 | 张五 | 工学部 | 19 | 1 | 广州 | s1000003 | c1003 | 3.20 |
| s1000003 | 张五 | 工学部 | 19 | 1 | 广州 | s1000003 | c1004 | 2.40 |
| s1000003 | 张五 | 工学部 | 19 | 1 | 广州 | s1000003 | c1005 | 4.90 |
| s1000003 | 张五 | 工学部 | 19 | 1 | 广州 | s1000003 | c1006 | 4.90 |
| s1000003 | 张五 | 工学部 | 19 | 1 | 广州 | s1000003 | c1007 | 3.10 |
| s1000003 | 张五 | 工学部 | 19 | 1 | 广州 | s1000003 | c1008 | 3.30 |
| s1000004 | 张六 | 工学部 | 17 | 1 | 深圳 | s1000004 | c1001 | 3.20 |
| s1000004 | 张六 | 工学部 | 17 | 1 | 深圳 | s1000004 | c1002 | 4.90 |
| s1000004 | 张六 | 工学部 | 17 | 1 | 深圳 | s1000004 | c1003 | 3.30 |
| s1000004 | 张六 | 工学部 | 17 | 1 | 深圳 | s1000004 | c1004 | 2.80 |
| s1000004 | 张六 | 工学部 | 17 | 1 | 深圳 | s1000004 | c1005 | 4.90 |
| s1000004 | 张六 | 工学部 | 17 | 1 | 深圳 | s1000004 | c1006 | 3.20 |
| s1000004 | 张六 | 工学部 | 17 | 1 | 深圳 | s1000004 | c1007 | 4.90 |
| s1000004 | 张六 | 工学部 | 17 | 1 | 深圳 | s1000004 | c1008 | 3.90 |
| s1000005 | 张七 | 工学部 | 18 | 1 | 北京 | s1000005 | c1001 | 4.20 |
| s1000005 | 张七 | 工学部 | 18 | 1 | 北京 | s1000005 | c1002 | 4.90 |
| s1000005 | 张七 | 工学部 | 18 | 1 | 北京 | s1000005 | c1003 | 4.90 |
| s1000005 | 张七 | 工学部 | 18 | 1 | 北京 | s1000005 | c1004 | 2.70 |
| s1000005 | 张七 | 工学部 | 18 | 1 | 北京 | s1000005 | c1005 | 3.90 |
| s1000005 | 张七 | 工学部 | 18 | 1 | 北京 | s1000005 | c1006 | 2.10 |
| s1000005 | 张七 | 工学部 | 18 | 1 | 北京 | s1000005 | c1007 | 4.90 |
| s1000005 | 张七 | 工学部 | 18 | 1 | 北京 | s1000005 | c1008 | 4.90 |
| s1000006 | 张八 | 工学部 | 19 | 1 | 上海 | s1000006 | c1001 | 4.90 |
| s1000006 | 张八 | 工学部 | 19 | 1 | 上海 | s1000006 | c1002 | 4.90 |
| s1000006 | 张八 | 工学部 | 19 | 1 | 上海 | s1000006 | c1003 | 3.50 |
| s1000006 | 张八 | 工学部 | 19 | 1 | 上海 | s1000006 | c1004 | 3.40 |
| s1000006 | 张八 | 工学部 | 19 | 1 | 上海 | s1000006 | c1005 | 4.90 |
| s1000006 | 张八 | 工学部 | 19 | 1 | 上海 | s1000006 | c1006 | 3.40 |
| s1000006 | 张八 | 工学部 | 19 | 1 | 上海 | s1000006 | c1007 | 3.20 |
| s1000006 | 张八 | 工学部 | 19 | 1 | 上海 | s1000006 | c1008 | 4.90 |
| s1000007 | 张九 | 工学部 | 20 | 1 | 广州 | s1000007 | c1001 | 3.20 |
| s1000007 | 张九 | 工学部 | 20 | 1 | 广州 | s1000007 | c1002 | 3.40 |
| s1000007 | 张九 | 工学部 | 20 | 1 | 广州 | s1000007 | c1003 | 1.90 |
| s1000007 | 张九 | 工学部 | 20 | 1 | 广州 | s1000007 | c1004 | 3.00 |
| s1000007 | 张九 | 工学部 | 20 | 1 | 广州 | s1000007 | c1005 | 3.60 |
| s1000007 | 张九 | 工学部 | 20 | 1 | 广州 | s1000007 | c1006 | 4.90 |
| s1000007 | 张九 | 工学部 | 20 | 1 | 广州 | s1000007 | c1007 | 3.50 |
| s1000007 | 张九 | 工学部 | 20 | 1 | 广州 | s1000007 | c1008 | 4.90 |
| s1000008 | 张十 | 工学部 | 18 | 1 | 深圳 | s1000008 | c1001 | 3.40 |
| s1000008 | 张十 | 工学部 | 18 | 1 | 深圳 | s1000008 | c1002 | 4.90 |
| s1000008 | 张十 | 工学部 | 18 | 1 | 深圳 | s1000008 | c1003 | 3.20 |
| s1000008 | 张十 | 工学部 | 18 | 1 | 深圳 | s1000008 | c1004 | 2.60 |
| s1000008 | 张十 | 工学部 | 18 | 1 | 深圳 | s1000008 | c1005 | 4.90 |
| s1000008 | 张十 | 工学部 | 18 | 1 | 深圳 | s1000008 | c1006 | 3.90 |
| s1000008 | 张十 | 工学部 | 18 | 1 | 深圳 | s1000008 | c1007 | 3.90 |
| s1000008 | 张十 | 工学部 | 18 | 1 | 深圳 | s1000008 | c1008 | 4.90 |
| s1000009 | 李三 | 工学部 | 17 | 1 | 北京 | s1000009 | c1001 | 3.70 |
| s1000009 | 李三 | 工学部 | 17 | 1 | 北京 | s1000009 | c1002 | 2.50 |
| s1000009 | 李三 | 工学部 | 17 | 1 | 北京 | s1000009 | c1003 | 3.10 |
| s1000009 | 李三 | 工学部 | 17 | 1 | 北京 | s1000009 | c1004 | 4.90 |
| s1000009 | 李三 | 工学部 | 17 | 1 | 北京 | s1000009 | c1005 | 2.90 |
| s1000009 | 李三 | 工学部 | 17 | 1 | 北京 | s1000009 | c1006 | 3.10 |
| s1000009 | 李三 | 工学部 | 17 | 1 | 北京 | s1000009 | c1007 | 4.90 |
| s1000009 | 李三 | 工学部 | 17 | 1 | 北京 | s1000009 | c1008 | 1.90 |
| s1000010 | 李四 | 工学部 | 16 | 1 | 上海 | s1000010 | c1001 | 3.50 |
| s1000010 | 李四 | 工学部 | 16 | 1 | 上海 | s1000010 | c1002 | 4.60 |
| s1000010 | 李四 | 工学部 | 16 | 1 | 上海 | s1000010 | c1003 | 3.90 |
| s1000010 | 李四 | 工学部 | 16 | 1 | 上海 | s1000010 | c1004 | 3.90 |
| s1000010 | 李四 | 工学部 | 16 | 1 | 上海 | s1000010 | c1005 | 4.90 |
| s1000010 | 李四 | 工学部 | 16 | 1 | 上海 | s1000010 | c1006 | 3.30 |
| s1000010 | 李四 | 工学部 | 16 | 1 | 上海 | s1000010 | c1007 | 3.60 |
| s1000010 | 李四 | 工学部 | 16 | 1 | 上海 | s1000010 | c1008 | 4.90 |
| s1000011 | 李五 | 工学部 | 19 | 1 | 广州 | NULL | NULL | NULL |
+----------+--------+-----------+------+------+--------+----------+-------+-------+
执行where后的条件: a.sno is null
后返回的记录即为所需要的记录。
6.总结
虽然本次的测试的记录数量并不多,但是可以看出来not in是比not exists慢的,而无论是两者中的哪一个都没有left join快。