1、如下图,虽然图中的type,有个表没用到索引,但是这个表数据很小,才被检索了115条数据,而最多的那张表也只被检索了2108条数据,而且还用上索引了,但是这条sql执行了298s,如何优化
这里就涉及到连表查的时候会用到的算法,笛卡尔积
顾名思义, 这个概念得名于笛卡儿. 在数学中,两个集合 X 和 Y 的笛卡儿积(Cartesian product),又称直积,表示为 X × Y,是其第一个对象是 X 的成员和第二个对象是 Y 的一个成员的所有可能的有序对.
假设集合A={a,b},集合B={0,1,2},则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1), (b,2)}。可以扩展到多个集合的情况。类似的例子有,如果A表示某学校学生的集合,B表示该学校所有课程的集合,则A与B的笛卡尔积表示所有可能的选课情况。
也就是说,当我们连表的时候,数据库会把我们两张表 on 条件下的所有可能性都给列出来,所以上图中看似只是检索了2108和115,但是其实组合的可能性是 2108的115次方,然后从这些数据里再进行where 条件的检索,所以我们的目标是,尽可能减少每个连表所检索的条数,即使看似几千条数据,相乘之后便是几十亿的数据量了
不过在连表查中,虽然有笛卡尔积,但是也会根据 on 后面的条件先一步来筛选数据,而不是直接将连表的检索数据进行笛卡尔积
比如一条sql如下
select p.*, a.* from pay as p left join account a on p.uid=a.id where p.time between '2020-03-15 00:00:00' and '2020-03-20 00:00:00' and a.uid between 1000 and 30000 group by a.uid order by p.time desc
上面sql说的是,一张储值表 pay,一张用户表account,连表查这两张表,将储值时间在2020-03-15 00:00:00 到 2020-03-20 00:00:00 且 用户uid在 1000 到 30000 的数据拿出来,然后根据用户来分组
首先我们看看mysql会怎么执行这条sql,具体文档路径是 https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008/ms189499(v=sql.100)?redirectedfrom=MSDN
我就直接摘出来了
1、执行from后面的,也就是先连上pay这张表
2、然后是on,也就是p.uid=a.id 先把这个过滤条件记上,如果有索引,就加载索引
3、然后是join,也就是连上 account这张表,如果sql是好几张表,就连上好几张表,这时候加上2的条件,就初步筛选出来一部分数据,这里会使用笛卡尔积,这里如果pay表和account表很大,那这部分数据筛出来也是会很庞大也会耗时
4、然后是where,从3中进一步筛选,有索引的话会更快
5、然后是group by
6、最后是order by
从上面步骤就知道第3步,事实上我们是不需要先把pay表和account都匹配一遍的,所以,如果我们使用子查询把where条件提前,就能避免这个问题
sql修改如下
select p.*, a* from (select * from payment where time between '2020-03-15 00:00:00' and '2020-03-20 00:00:00') as p left join (select * from account where uid between 1000 and 30000) as a on p.uid=a.id group by a.uid order by p.time
因为mysql会先执行子查询,在子查询中,payment表和account表已经被过滤了好多数据量,然后再进行主查询,这样就能把时间降下来
通过这个修改,上面我遇到的sql耗时问题,在我还没给表加索引的情况下,执行时间已经从298s降到了11s,估计加上索引还会更快
Processing Order of the SELECT statement
The following steps show the processing order for a SELECT statement.
1.FROM
2.ON
3.JOIN
4.WHERE
5.GROUP BY
6.WITH CUBE or WITH ROLLUP
7.HAVING
8.SELECT
9.DISTINCT
10.ORDER BY
11.TOP