SQL日报周报统计是业务系统中非常常见的需求,通常需要对一定时间范围内的业务数据进行聚合计算,当数据量达到百万甚至千万级别时,普通的统计查询往往会消耗大量时间,甚至出现查询超时的情况。时间分区和覆盖索引是两种针对性很强的优化方案,能够从减少扫描数据量和避免回表两个维度提升查询性能。

时间分区优化方案
时间分区是指按照时间维度将大表拆分为多个小的分区表,查询时只需要扫描对应时间范围的分区,不需要全表扫描。常见的分区类型有范围分区,适合按日期统计的场景。
分区表设计示例
以订单统计表为例,按月份进行范围分区,建表语句如下:
-- 创建订单统计表,按订单时间进行月度范围分区
CREATE TABLE order_stat (
order_id INT NOT NULL,
user_id INT NOT NULL,
order_amount DECIMAL(10,2) NOT NULL,
order_time DATE NOT NULL,
PRIMARY KEY (order_id, order_time)
) PARTITION BY RANGE (YEAR(order_time) * 100 + MONTH(order_time)) (
PARTITION p202401 VALUES LESS THAN (202402),
PARTITION p202402 VALUES LESS THAN (202403),
PARTITION p202403 VALUES LESS THAN (202404),
PARTITION p202404 VALUES LESS THAN (202405),
PARTITION p_max VALUES LESS THAN MAXVALUE
);
分区查询效果
统计2024年3月的日报数据时,查询语句只需要扫描p202403分区,不需要访问其他月份的数据:
-- 统计2024年3月每日订单量和总金额
SELECT
DATE(order_time) AS stat_date,
COUNT(order_id) AS order_count,
SUM(order_amount) AS total_amount
FROM order_stat
WHERE order_time >= '2024-03-01' AND order_time < '2024-04-01'
GROUP BY DATE(order_time)
ORDER BY stat_date;
可以通过EXPLAIN语句查看分区裁剪效果,确认查询只扫描了目标分区:
EXPLAIN SELECT COUNT(order_id) FROM order_stat WHERE order_time >= '2024-03-01' AND order_time < '2024-04-01';
覆盖索引优化方案
覆盖索引是指索引包含了查询所需要的所有字段,查询时不需要回表查询数据行,直接从索引中获取结果,减少IO消耗。对于统计类查询,通常只需要少数几个字段的聚合结果,非常适合使用覆盖索引。
覆盖索引创建示例
针对上面的订单统计需求,创建包含order_time、order_id、order_amount的覆盖索引:
-- 创建覆盖索引,包含统计需要的全部字段 CREATE INDEX idx_order_stat_cover ON order_stat (order_time, order_id, order_amount);
覆盖索引查询效果
同样的日报统计查询,使用覆盖索引后,查询过程不需要访问表数据,直接从索引中获取聚合所需的内容:
-- 使用覆盖索引的统计查询
SELECT
DATE(order_time) AS stat_date,
COUNT(order_id) AS order_count,
SUM(order_amount) AS total_amount
FROM order_stat
WHERE order_time >= '2024-03-01' AND order_time < '2024-04-01'
GROUP BY DATE(order_time)
ORDER BY stat_date;
通过EXPLAIN可以看到查询的Extra列出现Using index标识,说明使用了覆盖索引:
EXPLAIN SELECT DATE(order_time), COUNT(order_id), SUM(order_amount) FROM order_stat WHERE order_time >= '2024-03-01' AND order_time < '2024-04-01' GROUP BY DATE(order_time);
两种方案结合使用
时间分区和覆盖索引可以结合使用,进一步提升统计查询的性能。分区减少需要扫描的数据范围,覆盖索引避免回表,两者协同作用效果更明显。
结合使用的注意事项:
- 分区键要包含在索引的最前面,保证分区裁剪和索引使用都能生效
- 定期添加新的分区,避免数据写入到默认的最大分区中
- 定期分析表的统计信息,保证查询优化器能选择最优的执行计划
优化效果对比
以下是某业务系统订单表优化前后的统计查询性能对比:
| 优化方案 | 数据量 | 日报统计耗时 | 周报统计耗时 |
|---|---|---|---|
| 无优化 | 1200万 | 8.2秒 | 21.5秒 |
| 仅时间分区 | 1200万 | 1.7秒 | 4.3秒 |
| 仅覆盖索引 | 1200万 | 2.1秒 | 5.1秒 |
| 时间分区+覆盖索引 | 1200万 | 0.4秒 | 1.2秒 |
从对比数据可以看出,两种方案结合使用能够让SQL日报周报统计的查询速度提升数十倍,完全满足业务实时生成报表的需求。