冗余数据JOIN导致的慢SQL优化一例

简介: CASE 一个这样的查询,每个表都只有几千条数据,但是查询非常慢,几十秒不出结果。 select distinct abc.pro_col1, abc.col3 from t0 p INNER JOIN t1 abc on p.id=abc.par_col2

CASE

一个这样的查询,每个表都只有几千条数据,但是查询非常慢,几十秒不出结果。

select  
distinct abc.pro_col1, abc.col3  
from  
t0 p  
INNER JOIN t1 abc 
  on p.id=abc.par_col2
inner join t2 s 
  on  s.col3=abc.col3  
inner join t3 po 
  on  po.id=s.col4 
where p.state=2 and po.state=3 
order by abc.pro_col1, abc.col3; 

优化方法

从语义来看,这条SQL是在经过几个JOIN后取其中一个表的两个字段的唯一值。

但是每一次关联,都可能产生冗余的值,所以导致了结果集越来越庞大。

修改建议,每一次JOIN都输出唯一值,减少冗余。

select 
distinct pro_col1, col3 from
(
select 
distinct t1.pro_col1, t1.col3, s.col4 from 
(
select 
distinct abc.pro_col1, abc.col3 from 
t1 abc INNER JOIN t0 p    
on (p.id = abc.par_col2 and p.state=2)
) t1
inner join t2 s 
on (s.col3 = t1.col3)
) t2
inner join t3 po   
on (po.id = t2.col4 and po.state=3)
order by t2.pro_col1, t2.col3  ;

修改后几十毫秒可以输出结果。

重现

postgres=# create table rt1(id int, info text);
CREATE TABLE
postgres=# create table rt2(id int, info text);
CREATE TABLE
postgres=# create table rt3(id int, info text);
CREATE TABLE
postgres=# create table rt4(id int, info text);
CREATE TABLE

postgres=# insert into rt1 select generate_series(1,1000),'test';
INSERT 0 1000
postgres=# insert into rt2 select 1,'test' from generate_series(1,1000);
INSERT 0 1000
postgres=# insert into rt3 select 1,'test' from generate_series(1,1000);
INSERT 0 1000
postgres=# insert into rt4 select 1,'test' from generate_series(1,1000);
INSERT 0 1000

以下查询,每次JOIN都产生大量的冗余数据,越到后面的JOIN,冗余越多,导致的查询非常漫长。

postgres=# explain select distinct rt1.id from rt1 join rt2 on rt1.id=rt2.id join rt3 on rt2.id=rt3.id join rt4 on rt3.id=rt4.id;
                                           QUERY PLAN                                            
-------------------------------------------------------------------------------------------------
 HashAggregate  (cost=145.25..155.25 rows=1000 width=4)
   Group Key: rt1.id
   ->  Hash Join  (cost=113.00..142.75 rows=1000 width=4)
         Hash Cond: (rt4.id = rt1.id)
         ->  Seq Scan on rt4  (cost=0.00..16.00 rows=1000 width=4)
         ->  Hash  (cost=100.50..100.50 rows=1000 width=12)
               ->  Hash Join  (cost=70.75..100.50 rows=1000 width=12)
                     Hash Cond: (rt3.id = rt1.id)
                     ->  Seq Scan on rt3  (cost=0.00..16.00 rows=1000 width=4)
                     ->  Hash  (cost=58.25..58.25 rows=1000 width=8)
                           ->  Hash Join  (cost=28.50..58.25 rows=1000 width=8)
                                 Hash Cond: (rt2.id = rt1.id)
                                 ->  Seq Scan on rt2  (cost=0.00..16.00 rows=1000 width=4)
                                 ->  Hash  (cost=16.00..16.00 rows=1000 width=4)
                                       ->  Seq Scan on rt1  (cost=0.00..16.00 rows=1000 width=4)
(15 rows)

修改如下,可以很快的得到结果

postgres=# select distinct t2.id from 
(
select distinct t1.id from 
(select distinct rt1.id from rt1 join rt2 on rt1.id=rt2.id) t1
join 
rt3 on t1.id=rt3.id
) t2
join rt4 on t2.id=rt4.id
;
 id 
----
  1
(1 row)
Time: 2.052 ms

postgres=# explain select distinct t2.id from 
postgres-# (
postgres(# select distinct t1.id from 
postgres(# (select distinct rt1.id from rt1 join rt2 on rt1.id=rt2.id) t1
postgres(# join 
postgres(# rt3 on t1.id=rt3.id
postgres(# ) t2
postgres-# join rt4 on t2.id=rt4.id
postgres-# ;
                                                 QUERY PLAN                                                  
-------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=190.25..200.25 rows=1000 width=4)
   Group Key: rt1.id
   ->  Hash Join  (cost=158.00..187.75 rows=1000 width=4)
         Hash Cond: (rt4.id = rt1.id)
         ->  Seq Scan on rt4  (cost=0.00..16.00 rows=1000 width=4)
         ->  Hash  (cost=145.50..145.50 rows=1000 width=4)
               ->  HashAggregate  (cost=125.50..135.50 rows=1000 width=4)
                     Group Key: rt1.id
                     ->  Hash Join  (cost=93.25..123.00 rows=1000 width=4)
                           Hash Cond: (rt3.id = rt1.id)
                           ->  Seq Scan on rt3  (cost=0.00..16.00 rows=1000 width=4)
                           ->  Hash  (cost=80.75..80.75 rows=1000 width=4)
                                 ->  HashAggregate  (cost=60.75..70.75 rows=1000 width=4)
                                       Group Key: rt1.id
                                       ->  Hash Join  (cost=28.50..58.25 rows=1000 width=4)
                                             Hash Cond: (rt2.id = rt1.id)
                                             ->  Seq Scan on rt2  (cost=0.00..16.00 rows=1000 width=4)
                                             ->  Hash  (cost=16.00..16.00 rows=1000 width=4)
                                                   ->  Seq Scan on rt1  (cost=0.00..16.00 rows=1000 width=4)
(19 rows)

Time: 0.750 ms

小结

这种SQL,如果要改内核的话,可以对统计信息进行分析(每个字段都有n_distinct),并对其进行query rewrite,得到同样的结果。

postgres=# \d pg_stats
          View "pg_catalog.pg_stats"
         Column         |   Type   | Modifiers 
------------------------+----------+-----------
 schemaname             | name     | 
 tablename              | name     | 
 attname                | name     | 
 inherited              | boolean  | 
 null_frac              | real     | 
 avg_width              | integer  | 
 n_distinct             | real     | 
 most_common_vals       | anyarray | 
 most_common_freqs      | real[]   | 
 histogram_bounds       | anyarray | 
 correlation            | real     | 
 most_common_elems      | anyarray | 
 most_common_elem_freqs | real[]   | 
 elem_count_histogram   | real[]   | 

祝大家玩得开心,欢迎随时来 阿里云促膝长谈业务需求 ,恭候光临

阿里云的小伙伴们加油,努力 做好内核与服务,打造最贴地气的云数据库

目录
相关文章
|
4月前
|
SQL 人工智能 JSON
Flink 2.1 SQL:解锁实时数据与AI集成,实现可扩展流处理
简介:本文整理自阿里云高级技术专家李麟在Flink Forward Asia 2025新加坡站的分享,介绍了Flink 2.1 SQL在实时数据处理与AI融合方面的关键进展,包括AI函数集成、Join优化及未来发展方向,助力构建高效实时AI管道。
847 43
|
4月前
|
SQL 人工智能 JSON
Flink 2.1 SQL:解锁实时数据与AI集成,实现可扩展流处理
本文整理自阿里云的高级技术专家、Apache Flink PMC 成员李麟老师在 Flink Forward Asia 2025 新加坡[1]站 —— 实时 AI 专场中的分享。将带来关于 Flink 2.1 版本中 SQL 在实时数据处理和 AI 方面进展的话题。
310 0
Flink 2.1 SQL:解锁实时数据与AI集成,实现可扩展流处理
|
5月前
|
SQL
SQL如何只让特定列中只显示一行数据
SQL如何只让特定列中只显示一行数据
|
2月前
|
SQL 存储 监控
SQL日志优化策略:提升数据库日志记录效率
通过以上方法结合起来运行调整方案, 可以显著地提升SQL环境下面向各种搜索引擎服务平台所需要满足标准条件下之数据库登记作业流程综合表现; 同时还能确保系统稳健运行并满越用户体验预期目标.
216 6
|
9月前
|
SQL 自然语言处理 数据库
【Azure Developer】分享两段Python代码处理表格(CSV格式)数据 : 根据每列的内容生成SQL语句
本文介绍了使用Python Pandas处理数据收集任务中格式不统一的问题。针对两种情况:服务名对应多人拥有状态(1/0表示),以及服务名与人名重复列的情况,分别采用双层for循环和字典数据结构实现数据转换,最终生成Name对应的Services列表(逗号分隔)。此方法高效解决大量数据的人工处理难题,减少错误并提升效率。文中附带代码示例及执行结果截图,便于理解和实践。
253 4
|
7月前
|
SQL 存储 自然语言处理
SQL的解析和优化的原理:一条sql 执行过程是什么?
SQL的解析和优化的原理:一条sql 执行过程是什么?
SQL的解析和优化的原理:一条sql 执行过程是什么?
|
5月前
|
SQL
SQL中如何删除指定查询出来的数据
SQL中如何删除指定查询出来的数据
|
5月前
|
SQL 关系型数据库 MySQL
SQL如何对不同表的数据进行更新
本文介绍了如何将表A的Col1数据更新到表B的Col1中,分别提供了Microsoft SQL和MySQL的实现方法,并探讨了多表合并后更新的优化方式,如使用MERGE语句提升效率。适用于数据库数据同步与批量更新场景。
|
7月前
|
SQL 数据挖掘 关系型数据库
【SQL 周周练】一千条数据需要做一天,怎么用 SQL 处理电表数据(如何动态构造自然月)
题目来自于某位发帖人在某 Excel 论坛的求助,他需要将电表缴费数据按照缴费区间拆开后再按月份汇总。当时用手工处理数据,自称一千条数据就需要处理一天。我将这个问题转化为 SQL 题目。
267 12
|
6月前
|
SQL DataWorks 数据管理
SQL血缘分析实战!数据人必会的3大救命场景
1. 开源工具:Apache Atlas(元数据管理)、Spline(血缘追踪) 2. 企业级方案:阿里DataWorks血缘分析、腾讯云CDW血缘引擎 3. 自研技巧:在ETL脚本中植入版本水印,用注释记录业务逻辑变更 📌 重点总结: