很多基础的东西可以拿来细说一下:
假设X和Y两个表如下:
mysql> select * from x;
+----+------+
| id | a |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
| 7 | 7 |
| 8 | 8 |
| 9 | 9 |
| 10 | 10 |
+----+------+
10 rows in set (0.00 sec)
mysql> select * from y;
+----+------+
| id | a |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+------+
3 rows in set (0.00 sec)
如果写成exists那么,这两种是一样的结果。由于Y表只有3行数据库。X表也只有10条。不会有性能问题。
实际上这样写,子查询有一条或者是1万条都是 只看1条。仅做一次判断。然后就执行父查询,几乎没有差别。
mysql> select * from x where exists (select *from y where y.id=1);
+----+------+
| id | a |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
| 7 | 7 |
| 8 | 8 |
| 9 | 9 |
| 10 | 10 |
+----+------+
10 rows in set (0.00 sec)
mysql> select * from x where exists (select *from y);
+----+------+
| id | a |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
| 7 | 7 |
| 8 | 8 |
| 9 | 9 |
| 10 | 10 |
+----+------+
10 rows in set (0.00 sec)
而实际工作中X和表Y表都会很大。而且实际工作中更多的是下面这种写法。这种关联子查询的方式。
我见到的多是下面这种。开发就写成这样了。我一直觉得这其实属于数据库表设计问题。
那么这种就是返回了较少的表的全部数据。当然这种会随着两个表的数量上升以及关联的范围越来越大会变得越来越慢。
真实的情况是两个表结果集都很大。不可能只有3条数据的。当然这就和exists本身关系不大了。那就两个表直接关联好了。
失去了exists的意义。
mysql> select * from x where exists (select *from y where x.id=y.id);
+----+------+
| id | a |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+------+
3 rows in set (0.00 sec)
有时候这个较少的表也很大。比如这时候子查询是2条。而实际情况子查询可能200万或者是2000万。
mysql> select * from x where exists (select *from y where x.id=y.id and y.id>1);
+----+------+
| id | a |
+----+------+
| 2 | 2 |
| 3 | 3 |
+----+------+
2 rows in set (0.00 sec)
从日志中就可以看到,关联子查询不会再是子查询仅返回1行的那种优势了。随着结果集的变化而变化。
# Query_time: 0.000857 Lock_time: 0.000012 Rows_sent: 3 Rows_examined: 6
select * from x where exists (select *from y where x.id=y.id);
# Query_time: 0.001061 Lock_time: 0.000014 Rows_sent: 2 Rows_examined: 5
select * from x where exists (select *from y where x.id=y.id and y.id>1);
以上都有一些等价改写,不过我还是建议从设计出发来解决。
其实日常工作中遇到的都是基础问题,很难有什么高级问题。关注一下基础吧。
我的新书中有更多有价值的的经验。
《DBA实战手记》--薛晓刚 京东预售中。