107.SELECT语句带有日期的优化

1.SELECT语句优化

(1)场景描述

如下SQL返回一行数据,需要36s;
# Query_time: 36.046755  Lock_time: 0.000131 Rows_sent: 1  Rows_examined: 13157416
select count(*) from `underwriting`.`prptmain` where date_format(UPDATETIME,'%Y%m%d') = CURDATE()-INTERVAL 1 DAY;

(2)查看表的结构

show create table `underwriting`.`prptmain`;
CREATE TABLE `prptmain` (
  `CONTRACTNO` varchar(40) NOT NULL COMMENT '合同编号',
  `UUID` varchar(40) NOT NULL COMMENT '系统单据识别标记',
  `PROPOSALNO` varchar(40) DEFAULT NULL COMMENT '投保单档合同号',
  `POLICYNO` varchar(40) DEFAULT NULL COMMENT '保单档合同号(批单档用到)',
  `CURRENCY` varchar(3) DEFAULT NULL COMMENT '币别',
  `CLASSCODE` varchar(3) DEFAULT NULL COMMENT '险类代码',
  `RISKCODE` varchar(4) DEFAULT NULL COMMENT '险种代码',
  `RISKNAME` varchar(80) DEFAULT NULL COMMENT '险种名称',
  `CLAUSETYPE` varchar(6) DEFAULT NULL COMMENT '条款代码',
  `ACCEPTDATE` datetime DEFAULT NULL COMMENT '承保确认时间',
  `VALIDDATE` datetime DEFAULT NULL COMMENT '生效时间',
  `EXPIRYDATE` datetime DEFAULT NULL COMMENT '失效时间',
  `RELATEDNO` varchar(30) DEFAULT NULL COMMENT '关联单号',
  `RENEWALFLAG` varchar(2) DEFAULT NULL COMMENT '续保标志(-1:转保/0:新保/1:续保)',
  `LASTYEARPOLICYNO` varchar(50) DEFAULT NULL COMMENT '上年保单号',
  `TOTALSTANDARDPREMIUM` decimal(14,2) DEFAULT NULL COMMENT '总标准保费',
  `TOTALRISKPREMIUM` decimal(14,2) DEFAULT NULL COMMENT '总风险保费',
  `TOTALPREMIUM` decimal(14,2) DEFAULT NULL COMMENT '总签单保费',
  `NOTAXPREMIUM` decimal(14,2) DEFAULT NULL COMMENT '总签单保费(不含税)',
  `VAT` decimal(14,2) DEFAULT NULL COMMENT '增值税金额',
  `LOWESTDISCOUNT` decimal(8,4) DEFAULT NULL COMMENT '最低折扣(规则引擎整单参考优惠)',
  `SALEDISCOUNT` decimal(8,4) DEFAULT NULL COMMENT '销售折扣(用户填写的折扣,默认最低折扣)',
  `TOTALADJUSTRATE` decimal(8,4) DEFAULT NULL COMMENT '总浮动系数',
  `VEHCRATEWITHANTSCORE` decimal(8,4) DEFAULT NULL COMMENT '不计算车险分最低折扣系数',
  `COMMISSIONRATE` decimal(8,4) DEFAULT NULL COMMENT '手续费比例',
  `COMMISSIONAMOUNT` decimal(14,2) DEFAULT NULL COMMENT '手续费金额(含税)',
  `COMMISSIONNOTAXAMOUNT` decimal(14,2) DEFAULT NULL COMMENT '手续费金额(不含税)',
  `COMMISSIONVAT` decimal(14,2) DEFAULT NULL COMMENT '手续费税金',
  `EXPENSERATE` decimal(8,4) DEFAULT NULL COMMENT '展业费比例',
  `EXPENSEAMOUNT` decimal(14,2) DEFAULT NULL COMMENT '展业费金额(含税)',
  `EXPENSENOTAXAMOUNT` decimal(14,2) DEFAULT NULL COMMENT '展业费金额(不含税)',
  `EXPENSEVAT` decimal(8,4) DEFAULT NULL COMMENT '展业费税金',
  `PERFORMANCERATE` decimal(10,0) DEFAULT NULL,
  `SPECIALCONTRACTFLAG` varchar(2) DEFAULT NULL COMMENT '特殊业务标志',
  `SPECIALCONTRACTSUMMARY` varchar(60) DEFAULT NULL COMMENT '特殊业务说明',
  `REINSNO` varchar(60) DEFAULT NULL COMMENT '临分注记/临分号码',
  `REINSTYPE` varchar(1) DEFAULT NULL COMMENT '再保分保状况',
  `TOTALLOSSFLAG` varchar(1) DEFAULT NULL COMMENT '全损标志 0代表没有全损 1代表车损全损 2代表盗抢全损 ps:不可能出险盗抢和车损全损同时出现的情况',
  `POLICYPRINTTYPE` varchar(2) DEFAULT NULL COMMENT '保单打印类型 1-电子保单,2-监制保单',
  `POLICYPRINTNO` varchar(22) DEFAULT NULL COMMENT '保单打印流水号',
  `APPOINTAREACODE` varchar(12) DEFAULT NULL COMMENT '指定地区查询代码',
  `QUERYSEQUENCENO` varchar(50) DEFAULT NULL COMMENT '平台查询码',
  `CONFIRMSEQUENCENO` varchar(30) DEFAULT NULL COMMENT '平台确认码',
  `AGQUERYCODE` varchar(30) DEFAULT NULL COMMENT '中介校验查询码',
  `BUSINESSSTATUS` varchar(2) DEFAULT NULL COMMENT '业务状态',
  `ADDITIONALCOSTRATE` decimal(8,4) DEFAULT NULL COMMENT '附加费用率',
  `SUMAMOUNT` decimal(14,2) DEFAULT NULL COMMENT '总保额',
  `SUMSUBPREM` decimal(14,2) DEFAULT NULL COMMENT '总附加险保费',
  `SUMDISCOUNT` decimal(14,2) DEFAULT NULL COMMENT '总折扣金额',
  `PROPOSALPRINTTYPE` varchar(1) DEFAULT NULL COMMENT '投保单打印类型 1-电子投保单,2-监制投保单',
  `SUMTAX` decimal(14,2) DEFAULT NULL COMMENT '车船税',
  `VALIDATONCE` varchar(1) DEFAULT NULL COMMENT '即时生效',
  `QUERYSEQEXPIREDATE` datetime DEFAULT NULL COMMENT '平台投保查询码有效期',
  `OLDRISKCODE` varchar(4) DEFAULT NULL COMMENT '老险种代码',
  `HOPEDISCOUNT` decimal(8,4) DEFAULT NULL COMMENT '期望折扣',
  `UWDISCOUNT` decimal(8,4) DEFAULT NULL COMMENT '核保折扣',
  `DISCOUNT` decimal(8,4) DEFAULT NULL COMMENT '整单折扣率',
  `RECOMMENDISCOUNT` decimal(8,4) DEFAULT NULL COMMENT '建议折扣',
  `RULEID` varchar(12) DEFAULT NULL COMMENT '手续费规则id',
  `CREATETIME` datetime DEFAULT NULL COMMENT '创建时间',
  `UPDATETIME` datetime DEFAULT NULL COMMENT '修改时间',
  `REMARK` varchar(1000) DEFAULT NULL COMMENT '备注',
  `HIGHESTDISCOUNT` decimal(8,4) DEFAULT NULL COMMENT '最高折扣',
  `REINSSTATUS` varchar(1) DEFAULT NULL COMMENT '再保状态:1--未提交再保;2--已提交再保;3--再保审核通过;4--再保审核不通过',
  `UWYEARDAYS` int(3) DEFAULT NULL COMMENT '承保年度天数(提车险用)',
  `TRUCKUBISCORE` decimal(8,2) DEFAULT NULL COMMENT '车慧达评分',
  `TRUCKUBITRANCHE` varchar(8) DEFAULT NULL COMMENT '车慧达分档',
  `BURIEDCODE` varchar(40) DEFAULT NULL COMMENT '埋码(慧营销系统出单)',
  `RESPECIALIND` varchar(5) DEFAULT NULL COMMENT '高价车标识',
  PRIMARY KEY (`CONTRACTNO`) USING BTREE,
  KEY `idx_tmain_uuid` (`UUID`) USING BTREE,
  KEY `idx_tmain_proposalNo` (`PROPOSALNO`) USING BTREE,
  KEY `idx_tmain_validDate` (`VALIDDATE`) USING BTREE,
  KEY `idx_tmain_expiryDate` (`EXPIRYDATE`) USING BTREE,
  KEY `idx_tmain_policyNo` (`POLICYNO`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='投保单合同主表';

(3)查看执行计划可以知道,该SQL未使用索引,全表扫描。

explain select count(*) from `underwriting`.`prptmain` where date_format(UPDATETIME,'%Y%m%d') = CURDATE()-INTERVAL 1 DAY;
+----+-------------+----------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | prptmain | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 8969966 |   100.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

(4)检查数据分布

--检查非空数据
root@localhost [(none)]>select count(1)  from `underwriting`.`prptmain`where UPDATETIME is not null; 
+----------+
| count(1) |
+----------+
| 10487603 |
+----------+
1 row in set (30.56 sec)

--检查该字段为空的数据。
select count(1)  from `underwriting`.`prptmain`where UPDATETIME is null; 
root@localhost [(none)]>select count(1)  from `underwriting`.`prptmain`where UPDATETIME is null; 
+----------+
| count(1) |
+----------+
|  2771253 |
+----------+
1 row in set (6.28 sec)

--监控空值占比
select 2771253/(10487603+2771253);
+----------------------------+
| 2771253/(10487603+2771253) |
+----------------------------+
|                     0.2090 |
+----------------------------+
1 row in set (0.00 sec)

可以看出,空的数据比例20%,80%的数据非空,且非空间的数据,基本都是时间戳不重复。
因此可以在 UPDATETIME 上创建索引,提示该SQL的性能。

(5)SQL等价改写

select count(*) from `underwriting`.`prptmain` where date_format(UPDATETIME,'%Y%m%d') = CURDATE()-INTERVAL 1 DAY;
更改为:

select count(*) from `underwriting`.`prptmain` where date_format(UPDATETIME,'%Y%m%d') = CURDATE()-INTERVAL 1 DAY
union all
select count(*) from `underwriting`.`prptmain` where UPDATETIME between CURDATE()-INTERVAL 1 DAY and CURDATE();
+----------+
| count(*) |
+----------+
|    25712 |
|    25712 |
+----------+
2 rows in set (15.98 sec)

--SQL可以等价改写为:
select count(*) from `underwriting`.`prptmain` where UPDATETIME between CURDATE()-INTERVAL 1 DAY and CURDATE();

(6)查看SQL执行次数

--查看SQL执行次数,发现只执行一次,且未在缓存中保留SQL;
root@localhost [(none)]>select left(digest,9) as 'sql标识',digest_text as 'sql概况',schema_name '库名',concat(TRUNCATE(SUM_TIMER_WAIT/(select sum(SUM_TIMER_WAIT) 
    -> from performance_schema.events_statements_summary_by_digest)*100,2),'%') as  '时间占比', truncate(avg_timer_wait/1000000000,0) as '平均时间(ms)',count_star '执行次数',
    -> truncate(SUM_ROWS_EXAMINED/count_star,0) '平均扫描行数' ,truncate(SUM_ROWS_SENT/count_star,0) '平均返回行数',SUM_NO_INDEX_USED as '全表扫次数',
    -> sum_created_tmp_tables+sum_created_tmp_disk_tables as '临时表次数' 
    -> from performance_schema.events_statements_summary_by_digest 
    ->  where digest_text like 'select count(*) from `underwriting`.`prptmain` where date_format%' ;
Empty set (0.06 sec)

由于执行次数过少,且SQL文本未在缓冲区缓存,通过查看慢日志发现,虽然该执行次数少,但是每天都会执行,
且该表上其他SQL基于 UPDATETIME 字段的查询都是使用全表扫描,执行效率低,故对其进行优化。
优化方法为创建索引并更改SQL语句:

(7)优化

create index `idx_updatetime` on `underwriting`.`prptmain`   (updatetime) ;
root@localhost [(none)]>select count(*) from `underwriting`.`prptmain` where UPDATETIME between CURDATE()-INTERVAL 1 DAY and CURDATE();
+----------+
| count(*) |
+----------+
|    25710 |
+----------+
1 row in set (0.00 sec)


--如下SQL雷同:建索引后需要重新修改SQL才可以提升查询的效率。
# Query_time: 48.675085  Lock_time: 0.000201 Rows_sent: 17256  Rows_examined: 13157416
SET timestamp=1704139091;
select `CONTRACTNO`,`UUID`,`PROPOSALNO`,`POLICYNO`,`CURRENCY`,`CLASSCODE`,`RISKCODE`,`RISKNAME`,
`CLAUSETYPE`,`ACCEPTDATE`,`VALIDDATE`,`EXPIRYDATE`,`RELATEDNO`,`RENEWALFLAG`,`LASTYEARPOLICYNO`,
`TOTALSTANDARDPREMIUM`,`TOTALRISKPREMIUM`,`TOTALPREMIUM`,`NOTAXPREMIUM`,`VAT`,`LOWESTDISCOUNT`,
`SALEDISCOUNT`,`TOTALADJUSTRATE`,`VEHCRATEWITHANTSCORE`,`COMMISSIONRATE`,`COMMISSIONAMOUNT`,
`COMMISSIONNOTAXAMOUNT`,`COMMISSIONVAT`,`EXPENSERATE`,`EXPENSEAMOUNT`,`EXPENSENOTAXAMOUNT`,
`EXPENSEVAT`,`PERFORMANCERATE`,`SPECIALCONTRACTFLAG`,`SPECIALCONTRACTSUMMARY`,`REINSNO`,`REINSTYPE`,
`TOTALLOSSFLAG`,`POLICYPRINTTYPE`,`POLICYPRINTNO`,`APPOINTAREACODE`,`QUERYSEQUENCENO`,
`CONFIRMSEQUENCENO`,`AGQUERYCODE`,`BUSINESSSTATUS`,`ADDITIONALCOSTRATE`,`SUMAMOUNT`,`SUMSUBPREM`,
`SUMDISCOUNT`,`PROPOSALPRINTTYPE`,`SUMTAX`,`VALIDATONCE`,`QUERYSEQEXPIREDATE`,`OLDRISKCODE`,
`HOPEDISCOUNT`,`UWDISCOUNT`,`DISCOUNT`,`RECOMMENDISCOUNT`,`RULEID`,`CREATETIME`,`UPDATETIME`,
`REMARK`,`HIGHESTDISCOUNT`,`REINSSTATUS`,`UWYEARDAYS`,`TRUCKUBISCORE`,`TRUCKUBITRANCHE`,
`BURIEDCODE`,`RESPECIALIND` from `underwriting`.`prptmain` 
where date_format(UPDATETIME,'%Y%m%d')= CURDATE()-INTERVAL 1 DAY;

优化后等价改写为:
select `CONTRACTNO`,`UUID`,`PROPOSALNO`,`POLICYNO`,`CURRENCY`,`CLASSCODE`,`RISKCODE`,`RISKNAME`,
`CLAUSETYPE`,`ACCEPTDATE`,`VALIDDATE`,`EXPIRYDATE`,`RELATEDNO`,`RENEWALFLAG`,`LASTYEARPOLICYNO`,
`TOTALSTANDARDPREMIUM`,`TOTALRISKPREMIUM`,`TOTALPREMIUM`,`NOTAXPREMIUM`,`VAT`,`LOWESTDISCOUNT`,
`SALEDISCOUNT`,`TOTALADJUSTRATE`,`VEHCRATEWITHANTSCORE`,`COMMISSIONRATE`,`COMMISSIONAMOUNT`,
`COMMISSIONNOTAXAMOUNT`,`COMMISSIONVAT`,`EXPENSERATE`,`EXPENSEAMOUNT`,`EXPENSENOTAXAMOUNT`,
`EXPENSEVAT`,`PERFORMANCERATE`,`SPECIALCONTRACTFLAG`,`SPECIALCONTRACTSUMMARY`,`REINSNO`,`REINSTYPE`,
`TOTALLOSSFLAG`,`POLICYPRINTTYPE`,`POLICYPRINTNO`,`APPOINTAREACODE`,`QUERYSEQUENCENO`,
`CONFIRMSEQUENCENO`,`AGQUERYCODE`,`BUSINESSSTATUS`,`ADDITIONALCOSTRATE`,`SUMAMOUNT`,`SUMSUBPREM`,
`SUMDISCOUNT`,`PROPOSALPRINTTYPE`,`SUMTAX`,`VALIDATONCE`,`QUERYSEQEXPIREDATE`,`OLDRISKCODE`,
`HOPEDISCOUNT`,`UWDISCOUNT`,`DISCOUNT`,`RECOMMENDISCOUNT`,`RULEID`,`CREATETIME`,`UPDATETIME`,
`REMARK`,`HIGHESTDISCOUNT`,`REINSSTATUS`,`UWYEARDAYS`,`TRUCKUBISCORE`,`TRUCKUBITRANCHE`,
`BURIEDCODE`,`RESPECIALIND` from `underwriting`.`prptmain` 
where UPDATETIME between CURDATE()-INTERVAL 1 DAY and CURDATE();


+------------------------+------------------------+------------------------+------------------------+----------+-----------+----------+------------------------------------------------+------------+---------------------+---------------------+---------------------+-----------+-------------+------------------------+----------------------+------------------+--------------+--------------+---------+----------------+--------------+-----------------+----------------------+----------------+------------------+-----------------------+---------------+-------------+---------------+--------------------+------------+-----------------+---------------------+------------------------+---------+-----------+---------------+-----------------+----------------+-----------------+--------------------------------+--------------------------------+-------------+----------------+--------------------+-------------+------------+-------------+-------------------+----------+-------------+---------------------+-------------+--------------+------------+----------+------------------+------------+---------------------+---------------------+--------+-----------------+-------------+------------+---------------+-----------------+--------------------------------------+--------------+
25705 rows in set (0.31 sec)

优化前:48.67 sec
优化后:0.31 sec


查看执行计划:
root@localhost [(none)]>explain select `CONTRACTNO`,`UUID`,`PROPOSALNO`,`POLICYNO`,`CURRENCY`,`CLASSCODE`,`RISKCODE`,`RISKNAME`,
    -> `CLAUSETYPE`,`ACCEPTDATE`,`VALIDDATE`,`EXPIRYDATE`,`RELATEDNO`,`RENEWALFLAG`,`LASTYEARPOLICYNO`,
    -> `TOTALSTANDARDPREMIUM`,`TOTALRISKPREMIUM`,`TOTALPREMIUM`,`NOTAXPREMIUM`,`VAT`,`LOWESTDISCOUNT`,
    -> `SALEDISCOUNT`,`TOTALADJUSTRATE`,`VEHCRATEWITHANTSCORE`,`COMMISSIONRATE`,`COMMISSIONAMOUNT`,
    -> `COMMISSIONNOTAXAMOUNT`,`COMMISSIONVAT`,`EXPENSERATE`,`EXPENSEAMOUNT`,`EXPENSENOTAXAMOUNT`,
    -> `EXPENSEVAT`,`PERFORMANCERATE`,`SPECIALCONTRACTFLAG`,`SPECIALCONTRACTSUMMARY`,`REINSNO`,`REINSTYPE`,
    -> `TOTALLOSSFLAG`,`POLICYPRINTTYPE`,`POLICYPRINTNO`,`APPOINTAREACODE`,`QUERYSEQUENCENO`,
    -> `CONFIRMSEQUENCENO`,`AGQUERYCODE`,`BUSINESSSTATUS`,`ADDITIONALCOSTRATE`,`SUMAMOUNT`,`SUMSUBPREM`,
    -> `SUMDISCOUNT`,`PROPOSALPRINTTYPE`,`SUMTAX`,`VALIDATONCE`,`QUERYSEQEXPIREDATE`,`OLDRISKCODE`,
    -> `HOPEDISCOUNT`,`UWDISCOUNT`,`DISCOUNT`,`RECOMMENDISCOUNT`,`RULEID`,`CREATETIME`,`UPDATETIME`,
    -> `REMARK`,`HIGHESTDISCOUNT`,`REINSSTATUS`,`UWYEARDAYS`,`TRUCKUBISCORE`,`TRUCKUBITRANCHE`,
    -> `BURIEDCODE`,`RESPECIALIND` from `underwriting`.`prptmain` 
    -> where UPDATETIME between CURDATE()-INTERVAL 1 DAY and CURDATE();
+----+-------------+----------+------------+-------+----------------+----------------+---------+------+-------+----------+-----------------------+
| id | select_type | table    | partitions | type  | possible_keys  | key            | key_len | ref  | rows  | filtered | Extra                 |
+----+-------------+----------+------------+-------+----------------+----------------+---------+------+-------+----------+-----------------------+
|  1 | SIMPLE      | prptmain | NULL       | range | idx_updatetime | idx_updatetime | 6       | NULL | 48718 |   100.00 | Using index condition |
+----+-------------+----------+------------+-------+----------------+----------------+---------+------+-------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

由全表扫描,变成全表扫描,因此快。

2.总结

在带有日期时间戳的字段上执行范围查询,最好给该时间字段建立索引。否则容易全表扫描。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值