PostgreSQL使用USING的RIGHT JOIN执行过慢 如何优化查询性能
优化方案
核心优化逻辑
你当前性能瓶颈的根源是:CTE生成了全量的t1/t2/t3数据(合计5万行),但实际仅需要和t4的3000行a/b/c匹配的部分,无效数据占比极高,同时t2/t3的关联字段缺少对应索引导致关联效率低。可参考以下优化方案:
1. 前置过滤缩小CTE数据集
在t1/t2/t3的CTE中提前添加exists过滤,仅保留和t4的a/b/c匹配的行,直接把三个CTE的结果集从合计5万行压缩到最多几千行的有效数据,减少后续关联的计算量:
with -- 优先定义t4,方便后面CTE复用过滤 t4 as (select distinct a,b,c from tab4), -- 如果t4的a/b/c本身无重复可以去掉distinct t1 as ( select a,b,c,d from tab1 where exists (select 1 from t4 where tab1.a = t4.a and tab1.b = t4.b and tab1.c = t4.c) group by a,b,c,d ), t2 as ( select a,b,c,d from tab2 where exists (select 1 from t4 where tab2.a = t4.a and tab2.b = t4.b and tab2.c = t4.c) group by a,b,c,d ), t3 as ( select a,b,c,d from tab3 where exists (select 1 from t4 where tab3.a = t4.a and tab3.b = t4.b and tab3.c = t4.c) group by a,b,c,d ) -- 调整连接顺序,用最小的t4作为驱动表,逻辑和原SQL的right join完全等价 select * from t4 left join t1 using (a,b,c) left join t2 using (a,b,c,d) left join t3 using (a,b,c,d);
2. 新增关联字段联合索引
给t2、t3分别创建(a,b,c,d)的联合索引,给t1也创建同字段联合索引,关联时可以直接走索引查找,避免全表扫描,嵌套循环的效率会提升数倍。
3. 可选优化(针对不支持CTE内联的数据库)
如果使用的是较老版本的数据库(比如PostgreSQL12以下、MySQL8.0以下),CTE会默认物化降低效率,可以把t4、过滤后的t1/t2/t3改成临时表,给临时表的关联字段加上索引后再做关联,性能会进一步提升。
验证说明
上述方案改造后,所有参与关联的数据集都控制在数千行量级,加上索引加持,正常执行耗时可以降到3秒以内,符合性能预期。
内容的提问来源于stack exchange,提问作者Ivan Kolyhalov
相关产品推荐
相关产品推荐

