MySQL(总)MySQL索引

关于索引不走or的思考(完结)

2018-03-15  本文已影响1人  飞翔的Tallgeese

原语句(金融云trade库)

SELECT

    sum( amount )

FROM

    tra_trade

WHERE

    STATUS = 'success'

    AND partner_id != '*********************00008'

    AND partner_id != '*********************00074'

    AND partner_id != '*********************00009'

    AND date_format( update_time, '%y' ) = date_format( now( ), '%y' );

存在问题

1.存在!=,而且是3个

2.存在对时间函数的隐式转换

分析

1.date_format存在函数的隐式转换,无法优化

2.status列和partner_id列的选择性分别只有0.0000003和0.0001

3.连续3个!=,相当于not in

结论

对于索引列,及时创建联合索引也不会走;

实际测试也是如此;

所以在前面3.13的记录里面,认为无法对其进行优化。

转折

忽略了查询列,查询列的选择性达到了0.11

创建索引列和查询列的联合索引

ALTER TABLE `trade`.`tra_trade` ADD INDEX idx_status_partnerid_amount (`status`, `partner_id`, `amount`);

通过执行计划获知扫描行由24W行变成了4W行!

延伸

索引准则有一句:

不等空值还有or,索引失效要少用

这里很明显存在不等,反过来如果是3个等于的话,那么很明显也会存在or,但是索引确没有失效!

首先需要明白这句话对应的条件

tra_trade表是trade库中一张行数为24W的表,其中id,gid,trade_no选择性为1,partner_id选择性为0.0001

create table tra_trade_test as select id,gid,merchant_order_no,trade_no,amount,seller_user_id,payer_user_id,trade_name,status,partner_id

from tra_trade;

create index idx_test_0 on tra_trade_test(gid);

create index idx_test_1 on tra_trade_test(trade_no);

create index idx_test_2 on tra_trade_test(partner_id);

语句1

explain

select * from tra_trade_test where id>5000 and gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909');

毫无意外的走索引,即使索引列的2个条件都是范围查询,扫描行数仅仅为4行

删除gid索引

explain

select * from tra_trade_test where id>5000 and gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909');

仍然走索引,扫描行数暴增到12W,key_len只有8,说明只有id走了索引,此时虽然gid上面没有了索引,但走(id的)索引代价仍然比全表要小

语句2

将之前删掉的索引补回去

create index idx_test_0 on tra_trade_test(gid);

explain

select * from tra_trade_test where id>5000 or gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909')

执行计划中的type类型是index_merge,扫描行数为12W,index_merge的意思是索引执行了合并,这是因为我们此时应该把id和gid组成联合索引,这样效率会更高;但此时也没有走全表

删除gid索引

explain

select * from tra_trade_test where id>5000 or gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909')

毫无疑问的走全表了(所以可以看出,准则适用的其实是这里

语句3

将之前删掉的索引补回去

create index idx_test_0 on tra_trade_test(gid);

explain

select * from tra_trade_test

where gid = '*********************113e48fc'

or gid = '*********************113e48fe'

or gid = '*********************9b795a0'

or gid = '*********************113e4909';

该语句等效于

explain

select * from tra_trade_test where gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909');

毫无疑问的走了范围索引(可以看出,单列上存在索引,无论是否存在or,都会走索引,准则所说的情况并非这类语句

关于not in(!=)

语句4

explain

select gid from tra_trade_test

where gid != '*********************113e48fc'

and gid != '*********************113e48fe'

and gid != '*********************9b795a0'

and gid != '*********************113e4909';

explain

select id from tra_trade_test

where gid != '*********************113e48fc'

and gid != '*********************113e48fe'

and gid != '*********************9b795a0'

and gid != '*********************113e4909';

上述两条都走了索引

explain

select trade_no from tra_trade_test

where gid != '*********************113e48fc'

and gid != '*********************113e48fe'

and gid != '*********************9b795a0'

and gid != '*********************113e4909';

trade_no列上也建有索引,而且选择性为1,但是这条语句走了全表;

上一篇下一篇

猜你喜欢

热点阅读