维护一个交易系统,交易记录表tradelog包含交易流水号(tradeid)、交易员id(operator)、交易时间(t_modified)等字段。
建表语句如下:
create table 'tradelog' ('id' int(11) not null,'tradeid' varchar(32) default null,'operator' int(11) default null,'t_modified' datetime default null,primary key ('id'),key 'tradeid' ('tradeid'),key 't_modified' ('t_modified')
) engine = InnoDB default charset = utf8mb4;
假设已经记录了从2016年初到2018年底的所有数据,运营部门有一个需求:统计发生在所有年份中7月份的交易记录总数。
我们可以这样写:
select count(*) from tradelog where month(t_modified) = 7;
t_modified字段上有索引,但是会发现这条语句执行时间特别久,才返回结果。
但是MySQL规定:如果对字段做了函数计算,就不能使用索引。
也就是说:
条件为where t_modified = '2018-7-1'
时,可以用上索引,而改成where month(t_modified) = 7
的时候就不行了。
B+树同一层的兄弟节点是有序的,所以可以快速定位。
而当使用了month()函数,传入7时,其实B+树不知道接下来是取子节点还是兄弟节点。
所以说对索引字段做函数操作,优化器无法判断最终的结果是不是有序的,所以就会放弃使用搜索树,只能全部扫描该索引树。所以建议在查询时,尽量不要对字段进行操作
为了能够用上索引的快速定位能力,我们就要把SQL语句改成基于字段本身的范围查询:
select count(*) from tradelog where-> (t_modified >= '2016-7-1' and t_modified < '2016-8-1') or-> (t_modified >= '2017-7-1' and t_modified < '2017-8-1') or-> (t_modified >= '2018-7-1' and t_modified < '2018-8-1');
优化器在对于不改变有序性的函数上,也不会考虑使用索引。比如:对于select * from tradelog where id + 1 = 10000这个SQL语句,
这个+1不会改变有序性,但是优化器还是不能用id索引快速定位到9999这一行。所以需要我们在写SQL语句时,手动改写成where id = 10000 - 1才行。