基于TID列分组筛选单条记录的SQL查询优化需求
SQL优化:分组筛选逻辑改写以避免全表扫描
问题背景
需按SRN、GIO、FID字段分组,基于TID列值筛选组内记录:
- 当组内存在指定的两个
TID值时,仅保留组内rank为1的记录 - 其余不满足条件的组,所有记录均需保留
现有SQL因嵌套多CASE条件,导致数据库无法有效利用索引,触发全表扫描,数据量大时性能极差,需改写优化。
优化思路
核心是提前标记组状态,避免在过滤或窗口函数中嵌套复杂CASE逻辑,让数据库能利用分组字段的联合索引加速计算:
- 先通过分组聚合或窗口函数,一次性判断每个组是否包含目标TID组合
- 再针对组状态,结合rank值做筛选,减少不必要的计算开销
优化方案1:窗口函数预标记组状态
适合大部分数据库(MySQL 8.0+、PostgreSQL、SQL Server等),逻辑清晰且执行效率高:
WITH group_status AS ( SELECT t.*, -- 标记组内是否存在两个目标TID(替换为你的实际TID值) MAX(CASE WHEN TID = 'TARGET_TID_1' THEN 1 ELSE 0 END) OVER (PARTITION BY SRN, GIO, FID) AS has_tid1, MAX(CASE WHEN TID = 'TARGET_TID_2' THEN 1 ELSE 0 END) OVER (PARTITION BY SRN, GIO, FID) AS has_tid2, -- 按你的原逻辑计算组内rank(替换排序字段为实际规则) ROW_NUMBER() OVER (PARTITION BY SRN, GIO, FID ORDER BY sort_column DESC) AS rn FROM your_table t ), group_flag AS ( SELECT *, CASE WHEN has_tid1 = 1 AND has_tid2 = 1 THEN 1 ELSE 0 END AS is_target_group FROM group_status ) SELECT SRN, GIO, FID, TID, -- 按需选择输出字段 rn FROM group_flag WHERE is_target_group = 0 OR (is_target_group = 1 AND rn = 1);
优化点说明
- 用窗口函数一次性完成组内TID存在性判断和rank计算,避免多次扫描表
- 简单的CASE标记组状态,让数据库能更好地优化执行计划,若
SRN,GIO,FID有联合索引,可直接利用索引加速窗口分组 - 过滤条件逻辑直白,减少数据库计算负载
优化方案2:分组聚合预判断组状态
适合数据量极大的场景,先通过聚合得到组状态,再关联原表做筛选,进一步减少扫描量:
WITH group_check AS ( SELECT SRN, GIO, FID, -- 判断组内是否同时包含两个目标TID CASE WHEN COUNT(DISTINCT CASE WHEN TID IN ('TARGET_TID_1', 'TARGET_TID_2') THEN TID END) = 2 THEN 1 ELSE 0 END AS is_target_group FROM your_table GROUP BY SRN, GIO, FID ) SELECT t.*, ROW_NUMBER() OVER (PARTITION BY t.SRN, t.GIO, t.FID ORDER BY sort_column DESC) AS rn FROM your_table t JOIN group_check gc ON t.SRN = gc.SRN AND t.GIO = gc.GIO AND t.FID = gc.FID WHERE gc.is_target_group = 0 OR (gc.is_target_group = 1 AND rn = 1);
优化点说明
- 先通过一次分组聚合得到所有组的状态,若
SRN,GIO,FID有联合索引,聚合操作会非常高效 - 关联原表时可利用索引快速匹配,避免全表扫描
- 仅对需要的组计算rank,减少不必要的窗口函数计算
验证说明
两种方案均严格符合需求:
- 包含目标TID组合的组,仅保留rank=1的记录
- 不满足条件的组,所有记录完整保留
内容的提问来源于stack exchange,提问作者GIN
相关产品推荐
相关产品推荐

