sql拆解升级
一、代码审查、团队有个小伙子写的sql,真的头大
SELECT
id,
tenant_id,
store_id,
esp_org_id,
ifd_id,
dim_dish_id,
standard_price,
valuation_type,
weight_type,
portion_weight,
first_validate_time,
create_time,
update_time
FROM dwd_ifd_goods_standard_prices AS t
WHERE
t.ifd_id = 'bcc3d3e10e11ffbf6a61a3a675bf77'
AND t.id = IFNULL(
-- 子查询1:找<=指定时间的最新一条
(SELECT s1.id
FROM dwd_ifd_goods_standard_prices s1
WHERE ((s1.tenant_id = t.tenant_id) OR (s1.tenant_id IS NULL AND t.tenant_id IS NULL))
AND s1.first_validate_time <= '2026-04-09 14:47:06.748'
AND s1.ifd_id = t.ifd_id
ORDER BY s1.first_validate_time DESC
LIMIT 1),
-- 子查询2:找>指定时间的最早一条
(SELECT s2.id
FROM dwd_ifd_goods_standard_prices s2
WHERE ((s2.tenant_id = t.tenant_id) OR (s2.tenant_id IS NULL AND t.tenant_id IS NULL))
AND s2.first_validate_time > '2026-09 14:47:06.748'
AND s2.ifd_id = t.ifd_id
ORDER BY s2.first_validate_time ASC
LIMIT 1)
);
二、SQL 功能说明(你必须知道)
这条 SQL 的核心作用:
- 按
ifd_id + tenant_id分组 - 先找 生效时间 ≤ 指定时间 的最新一条价格
- 找不到就找 生效时间 > 指定时间 的最早一条价格
- 最终只返回这一条匹配的价格数据
三、性能问题分析(原 SQL 致命缺陷)
- 相关子查询:每扫描一行主表数据,就执行 2 次子查询,数据量大时极慢
- NULL 判断冗余:
(a=b or (a is null and b is null))可以用 MySQL 安全等于<=>简化 - 无索引:全表扫描,百万级数据直接卡死
四、最优优化方案(推荐直接使用)
1. 优化后 SQL(简洁 + 高效)
SELECT
id,
tenant_id,
store_id,
esp_org_id,
ifd_id,
dim_dish_id,
standard_price,
valuation_type,
weight_type,
portion_weight,
first_validate_time,
create_time,
update_time
FROM dwd_ifd_goods_standard_prices
WHERE
ifd_id = 'bcc3d3e10e11ffbf6a61a3a675bf77'
AND tenant_id <=> NULL
ORDER BY
-- 先按时间倒序(取<=时间的最新)
(first_validate_time <= '2026-04-09 14:47:06.748') DESC,
first_validate_time DESC,
-- 找不到再按时间正序(取>时间的最早)
first_validate_time ASC
LIMIT 1;
2. 必加索引(性能提升 100 倍 +)
CREATE INDEX idx_ifd_tenant_time ON dwd_ifd_goods_standard_prices (ifd_id, tenant_id, first_validate_time);
五、优化点总结
- 去掉相关子查询:从 O (n) 复杂度降到 O (1)
- 用 <=> 替代 NULL 复杂判断:代码更简洁,数据库优化器能命中索引
- 一条 ORDER BY + LIMIT 实现逻辑:数据库一次排序即可拿到目标数据
- 组合索引:
ifd_id + tenant_id + first_validate_time完美覆盖查询条件
#研发##数据人的面试交流地##春招至今,你收到几个面试了?#直接使用【优化后 SQL + 索引】,性能和可读性都达到最优