实际案例:“隐式转换”导致的索引失效

一、现象

1.1、表结构(数据库tidb)
CREATE TABLE `daily_customer_warn_data` (
  `id` bigint(15) NOT NULL AUTO_INCREMENT,
  `biz_uid` bigint(20) NOT NULL COMMENT '商业uid',
  `zz_uid` bigint(20) NOT NULL COMMENT '转转uid',
  `warn_type` int(10) NOT NULL DEFAULT '0' COMMENT '',
  `dt` varchar(10) NOT NULL COMMENT '数据归属日期,yyyy-MM-dd'
      
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_dt_pv1_pvr_keyword` (`dt`,`biz_uid`,`warn_type`)
)
1.2、SQL查询

mysql> select count(1) from daily_customer_warn_data where dt=date_sub(‘2021-04-30’,interval 8 day);
±---------+
| count(1) |
±---------+
| 39232 |
±---------+
1 row in set (15.16 sec)

没什么没有走uniq_dt_pv1_pvr_keyword来查询呢?

二、分析:

2.1、SQL的执行计划

mysql> explain
-> select
-> count(1)
-> from daily_customer_warn_data where dt=date_sub(‘2021-04-30’,interval 8 day);
±-----------------------±------------±-----±--------------------------------------------------------------------+
| id | count | task | operator info |
±-----------------------±------------±-----±--------------------------------------------------------------------+
| StreamAgg_10 | 1.00 | root | funcs:count(1) |
| └─Selection_14 | 13893928.80 | root | eq(cast(tdb_adstat.daily_customer_warn_data.dt), 2021-04-22) |
| └─TableReader_16 | 17367411.00 | root | data:TableScan_15 |
| └─TableScan_15 | 17367411.00 | cop | table:daily_customer_warn_data, range:[-inf,+inf], keep order:false |

2.2、原因:

eq(cast(tdb_adstat.daily_customer_warn_data.dt), 2021-04-22),cast(tdb_adstat.daily_customer_warn_data.dt) 是将dt转换成时间类型,这样索引就失效了

2.3、tidb文档

值得注意的是,TiDB 目前只支持比较符一端是列,另一端是常量,或可以计算成某一常量的情况,类似 year(birth_day) < 1992 的查询条件是不能利用索引的。还要注意应尽可能使用同一类型进行比较,以避免引入额外的 cast 操作而导致不能利用索引,如 user_id = 123456,如果 user_id 是字符串,需要将 123456 也写成字符串常量的形式。
4、优化:

explain
select
count(1)
from daily_customer_warn_data   where dt=date_sub('2021-04-30',interval 8 day);

±-----------------------±------------±-----±--------------------------------------------------------------------+
| id | count | task | operator info |
±-----------------------±------------±-----±--------------------------------------------------------------------+
| StreamAgg_10 | 1.00 | root | funcs:count(1) |
| └─Selection_14 | 13893928.80 | root | eq(cast(tdb_adstat.daily_customer_warn_data.dt), 2021-04-22) |
| └─TableReader_16 | 17367411.00 | root | data:TableScan_15 |
| └─TableScan_15 | 17367411.00 | cop | table:daily_customer_warn_data, range:[-inf,+inf], keep order:false |

三、思考

3.1 什么是隐式转换

当运算符与不同类型的操作数一起使用时,将进行类型转换以使操作数兼容。某些转换是隐式发生的。例如,MySQL会根据需要自动将字符串转换为数字,反之亦然。例如

 mysql> SELECT 1+'1';
+-------+
| 1+'1' |
+-------+
|     2 |
+-------+
  SELECT CONCAT(2,' test');
+-------------------+
| CONCAT(2,' test') |
+-------------------+
| 2 test            |
+-------------------+
3.2 产生的条件
  1. 两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 <=> 对两个 NULL 做比较时会返回1,这两种情况都不需要做类型转换
  2. 两个参数都是字符串,会按照字符串来比较,不做类型转换
  3. 两个参数都是整数,按照整数来比较,不做类型转换
  4. 十六进制的值和非数字做比较时,会被当做二进制串
  5. 有一个参数是 TIMESTAMP 或DATETIME,并且另外一个参数是常量,常量会被转换为 timestamp 有一个参数是 decimal 类型,
  6. 如果另外一个参数是decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal
    转换为浮点数进行比较 所有其他情况下,两个参数都会被转换为浮点数再进行比较
3.3 解决

MySQL 的类型转换函数 cast 和 convert,来明确的进行转换。

关注下,一起学习~~
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值