Postgresql优化--子查询优化

项目在进行技术压测过程中,遇到很多sql优化的问题,这方面需要了解数据库索引原理,数据库机制,数据库查询优化器,推荐一本书为《Postgresql技术内幕》,开始读很晦涩,但一旦理论联系实际之后,就很好理解。
不过,本人认为,数据库优化第一需要做的是要了解业务需求,在进行优化签,请开发人员先将sql的需求解说一下,然后,在理出业务逻辑,最后将业务场景和sql语句对应来看,进行优化,这样不仅能做到sql优化,还可以做到业务优化。
分享一个书中子查询优化,结合实际业务场景达到优化的效果。
首先,进行理论说明,取自于上述所说书中。
相关子查询:在子查询中引用外层表的列属性,导致外层表每获得一个元组,子查询就需要重新执行一次。
非相关子查询:在次查询语句是独立的,和外层表无直接关系,子查询可以单独执行一次,外层表可重复利用子查询结果。
Postgresql还基于子查询所在的位置和作用不同,将子查询分为子连接(Sublink)和子查询(SubQuery)。
按子句的位置不同,
出现在from关键字后的是子查询,出现在where/on等约束条件中或投影中的子句是子连接。
子查询:

select * from student ,(select * from score)as sc;

子连接:

select * from student where exists (select studentid from score);

其中,子连接是子查询的一种特殊情况,由于子连接出现在where/on等约束条件中,常伴随着ANY/ALL/EXISTS/SOME等谓语动词,pgy依据每种不同的谓语区分sublink类型。

实际应用业务场景:
先模糊获取子表的方案id集合,然后内关联主表,获取这些方案id的方案基本信息。
开发人员之前思路:

explain ANALYZE WITH a AS ( SELECT id FROM testA WHERE post_no = '00' ) SELECT
a.id,
name,
seq_no,
name,
create_time,
enable_date,
dline_date,
flag,
visible_flag,
status,
inherit_flag,
last_mod_time
FROM
	a
	INNER JOIN test b ON a.id = b.id

子表的数据量至少是主表的三倍,主表大概60w数据

按子连接提升原则:EXISTS类型的相关子连接会被提升,非相关子连接会形成子执行计划单独求解。

xue=> explain ANALYZE WITH a AS ( SELECT id FROM testA WHERE post_no = '00' )  SELECT
xue-> a.id,
xue-> name,
xue-> seq_no,
xue-> name,  
xue-> create_time,
xue-> enable_date,
xue-> dline_date,
xue-> flag,
xue->visible_flag,
xue->status,
xue-> inherit_flag,
xue-> no,
xue-> last_mod_time
xue-> FROM
xue-> a
xue->INNER JOIN test b ON a.id = b.id;
                                                                QUERY PLAN                                                                 
-------------------------------------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=61095.24..105350.98 rows=631634 width=208) (actual time=715.362..1784.994 rows=631311 loops=1)
   Hash Cond: ((a.scheme_id)::text = (b.scheme_id)::text)
   CTE a
     ->  Seq Scan on testA  (cost=0.00..20665.42 rows=631634 width=19) (actual time=0.022..240.878 rows=631311 loops=1)
           Filter: ((post_no)::text = '00'::text)
   ->  CTE Scan on a  (cost=0.00..12632.68 rows=631634 width=82) (actual time=0.024..500.024 rows=631311 loops=1)
   ->  Hash  (cost=20351.92..20351.92 rows=590792 width=145) (actual time=715.076..715.076 rows=588192 loops=1)
         Buckets: 32768  Batches: 32  Memory Usage: 3636kB
         ->  Seq Scan on testb b  (cost=0.00..20351.92 rows=590792 width=145) (actual time=0.012..346.506 rows=588192 loops=1)
 Planning time: 1.383 ms
 Execution time: 1851.771 ms
(11 rows)

可以看出,执行计划,是利用Hash Join,将内表hash化,降低算法的复杂度,避免了重复多次执行子查询的问题,但速度还是很慢。
利用子连接优化:

--------------------------------------------------------------------
xue=> explain ANALYZE
xue-> select 
xue-> id,
xue-> name,
xue-> seq_no,
xue-> name,
xue-> time,
xue-> enable_date,
xue-> dline_date,
xue-> flag,
xue-> visible_flag,
xue-> status 
xue-> from testB
xue-> where  EXISTS ( SELECT id,
xue(> name,
xue(> seq_no,
xue(> name,
xue(> create_time,
xue(> enable_date,
xue(> dline_date,flag,visible_flag,status,inherit_flag,
xue(> last_mod_time,
xue(> scheme_template_flag  FROM testa WHERE post_no = '00' ) 
xue-> ;
                                                            QUERY PLAN                                                             
-----------------------------------------------------------------------------------------------------------------------------------
 Result  (cost=0.03..20351.95 rows=590792 width=145) (actual time=0.028..434.934 rows=588192 loops=1)
   One-Time Filter: $0
   InitPlan 1 (returns $0)
     ->  Seq Scan on testb  (cost=0.00..20665.42 rows=631634 width=0) (actual time=0.017..0.017 rows=1 loops=1)
           Filter: ((post_no)::text = '00'::text)
   ->  Seq Scan on testa  (cost=0.03..20351.95 rows=590792 width=145) (actual time=0.009..296.965 rows=588192 loops=1)
 Planning time: 0.172 ms
 Execution time: 491.057 ms

优化使用EXITS类型的非相关子连接,子连接形成了一个单独的子执行计划,查询执行器会对这个子执行计划进行单独求解,求解结果作为一个
One-Time Filter 来决定是否对主表进行扫描,虽未做到“一次求解多次使用,但做到了一次求解决定全局”

你可能感兴趣的:(postgresql子查询优化,postgresql)