当前位置: 首页 > news >正文

SQL面试题:一个优化案例

问题描述

假如存在以下两个表:

CREATE TABLE `customer` (
`C_CUSTKEY` int NOT NULL,
`C_NAME` varchar(25) NOT NULL,
`C_ADDRESS` varchar(40) NOT NULL,
`C_NATIONKEY` int NOT NULL,
`C_PHONE` char(15) NOT NULL,
`C_ACCTBAL` decimal(15,2) NOT NULL,
`C_MKTSEGMENT` char(10) NOT NULL,
`C_COMMENT` varchar(117) NOT NULL,PRIMARY KEY (`C_CUSTKEY`)
) ENGINE=InnoDB;-- tpch.orders definition
CREATE TABLE `orders` (
`O_ORDERKEY` int NOT NULL,
`O_CUSTKEY` int NOT NULL,
`O_ORDERSTATUS` char(1) NOT NULL,
`O_TOTALPRICE` decimal(15,2) NOT NULL,
`O_ORDERDATE` date NOT NULL,
`O_ORDERPRIORITY` char(15) NOT NULL,
`O_CLERK` char(15) NOT NULL,
`O_SHIPPRIORITY` int NOT NULL,
`O_COMMENT` varchar(79) NOT NULL,
PRIMARY KEY (`O_ORDERKEY`)
) ENGINE=InnoDB;

请问,下面的查询语句应该如何优化?

select * 
from customer 
where c_custkey = (select max(o_custkey) from orders where subdate(o_orderdate, interval '1' DAY) < '2022-12-20');

思路分析

首先,customer 表的 c_custkey 字段是主键,查询条件是等值查找,已经最优了。

其次,子查询的条件字段没有索引,需要全表扫描。看一下执行计划:

EXPLAIN
select * from customer 
where c_custkey = (select max(o_custkey) 
from orders where subdate(o_orderdate, interval '1' DAY) < '2022-12-20');id|select_type|table |partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra                         |
--+-----------+------+----------+----+-------------+---+-------+---+----+--------+------------------------------+1|PRIMARY    |      |          |    |             |   |       |   |    |        |no matching row in const table|2|SUBQUERY   |orders|          |ALL |             |   |       |   |   1|   100.0|Using where                   |

因此主要优化这个子查询。

第一步,考虑子查询使用 o_orderdate 作为条件,可以基于这个字段创建索引,同时需要把 subdate 函数放到表达式右边,否则会导致索引失效。

CREATE INDEX idx_orders_date ON orders(o_orderdate);EXPLAIN
select * from customer 
where c_custkey = (select max(o_custkey) 
from orders where o_orderdate < adddate('2022-12-20', interval '1' DAY));id|select_type|table |partitions|type |possible_keys  |key            |key_len|ref|rows|filtered|Extra                         |
--+-----------+------+----------+-----+---------------+---------------+-------+---+----+--------+------------------------------+1|PRIMARY    |      |          |     |               |               |       |   |    |        |no matching row in const table|2|SUBQUERY   |orders|          |range|idx_orders_date|idx_orders_date|3      |   |   1|   100.0|Using index condition         |

对于 orders 表的访问类型变成了索引范围扫描(range),但是仍然不够理想,因为扫描完索引之后还需要回表查询 max(o_custkey)。

第二步,考虑利用覆盖索引优化,避免回表。可以基于 o_orderdate 和 o_custkey 创建一个复合索引。

CREATE INDEX idx_orders_date_cust ON orders(o_orderdate, o_custkey);EXPLAIN
select * from customer 
where c_custkey = (select max(o_custkey) 
from orders where o_orderdate < adddate('2022-12-20', interval '1' DAY));id|select_type|table |partitions|type |possible_keys                       |key                 |key_len|ref|rows|filtered|Extra                         |
--+-----------+------+----------+-----+------------------------------------+--------------------+-------+---+----+--------+------------------------------+1|PRIMARY    |      |          |     |                                    |                    |       |   |    |        |no matching row in const table|2|SUBQUERY   |orders|          |index|idx_orders_date,idx_orders_date_cust|idx_orders_date_cust|7      |   |   1|   100.0|Using where; Using index      |

第三步,考虑复合索引的字段顺序是否有更好的选择,也就是说 (o_orderdate, o_custkey) 还是 (o_custkey, o_orderdate)。

如果选择 (o_orderdate, o_custkey),通过覆盖索引获取数据时,需要找出 o_orderdate 小于 2022-12-21 的所有索引节点,然后遍历其中的 o_custkey,找出最大的值。

如果选择 (o_custkey, o_orderdate),通过覆盖索引获取数据时,需要按照 o_custkey 从大到小查找索引,找出其中 o_orderdate 小于 2022-12-21 的第一个索引节点即可。

MySQL 优化器目前不支持第二种选择。

http://www.lryc.cn/news/107591.html

相关文章:

  • 链表的总体涵盖以及无哨兵位单链表实现——【数据结构】
  • 网页版Java五子棋项目(一)websocket【服务器给用户端发信息】
  • 企业大数据可视化案例专题分享-入门
  • GoogLeNet卷积神经网络-笔记
  • 腾讯云TencentOS Server镜像系统常见问题解答
  • 【项目 进程13】2.28共享内存(1) 2.29共享内存(2)
  • Flask框架-流量控制:flask-limiter的使用
  • 【机器学习】西瓜书习题3.5Python编程实现线性判别分析,并给出西瓜数据集 3.0α上的结果
  • Elasticsearch:通过动态修剪实现更快的基数聚合
  • Webpack5 生产模式压缩图片ImageMinimizerPlugin
  • 时序预测 | Matlab实现基于BP神经网络的电力负荷预测模型
  • 基于回溯算法实现八皇后问题
  • Linux【网络编程】之深入理解TCP协议
  • 如何克服看到别人优于自己而感到的焦虑和迷茫?
  • 浅谈React中的ref和useRef
  • Linux C 获取主机网卡名及 IP 的几种方法
  • 解密外接显卡:笔记本能否接外置显卡?如何连接外接显卡?
  • list与erase()
  • Arcgis 分区统计majority参数统计问题
  • vue2+wangEditor5富文本编辑器(图片视频自定义上传七牛云/服务器)
  • shell脚本练习--安全封堵脚本,使用firewalld实现
  • 双端冒泡排序
  • 如何在Visual Studio Code中用Mocha对TypeScript进行测试
  • GO中Json的解析
  • chatgpt 提示词-关于数据科学的 75个词语
  • (自控原理)控制系统的数学模型
  • Webpack5 cacheGroups
  • 前端面试的游览器部分(5)每篇10题
  • 数据挖掘七种常用的方法汇总
  • 自然语言处理学习笔记(二)————语料库与开源工具