我的编程空间,编程开发者的网络收藏夹
学习永远不晚

PostgreSQL中remove_useless_joins的实现逻辑是怎样的

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

PostgreSQL中remove_useless_joins的实现逻辑是怎样的

这篇文章主要介绍“PostgreSQL中remove_useless_joins的实现逻辑是怎样的”,在日常操作中,相信很多人在PostgreSQL中remove_useless_joins的实现逻辑是怎样的问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中remove_useless_joins的实现逻辑是怎样的”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

query_planner代码片段:

     //...
 
     
     joinlist = remove_useless_joins(root, joinlist);//清除无用的外连接
 
     
     reduce_unique_semijoins(root);//消除半连接
 
     
     add_placeholders_to_base_rels(root);//在"base rels"中添加PH
 
      //...

一、数据结构

PlaceHolderVar
上一小节已介绍过PHInfo

 
 
 typedef struct PlaceHolderVar
 {
     Expr        xpr;
     Expr       *phexpr;         
     Relids      phrels;         
     Index       phid;           
     Index       phlevelsup;     
 } PlaceHolderVar;

SpecialJoinInfo

 
 
 typedef struct SpecialJoinInfo
 {
     NodeTag     type;
     Relids      min_lefthand;   
     Relids      min_righthand;  
     Relids      syn_lefthand;   
     Relids      syn_righthand;  
     JoinType    jointype;       
     bool        lhs_strict;     
     bool        delay_upper_joins;  
     
     bool        semi_can_btree; 
     bool        semi_can_hash;  
     List       *semi_operators; 
     List       *semi_rhs_exprs; 
 } SpecialJoinInfo;

二、源码解读

remove_useless_joins
清除无用的连接,比如以下的SQL语句:

select t1.dwbh 
from t_grxx t1 left join t_dwxx t2 on t1.dwbh = t2.dwbh;

左连接,而且t_dwxx.dwbh唯一,这样的连接是不需要的连接,直接查询t_grxx即可.
从执行计划来看,PG只对t_grxx进行扫描:

testdb=# explain verbose select t1.dwbh from t_grxx t1 left join t_dwxx t2 on t1.dwbh = t2.dwbh;
                             QUERY PLAN                             
--------------------------------------------------------------------
 Seq Scan on public.t_grxx t1  (cost=0.00..14.00 rows=400 width=38)
   Output: t1.dwbh
(2 rows)

源代码如下:

 
 List *
 remove_useless_joins(PlannerInfo *root, List *joinlist)
 {
     ListCell   *lc;
 
     
 restart:
     foreach(lc, root->join_info_list)//遍历连接信息链表
     {
         SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);
         int         innerrelid;
         int         nremoved;
 
         
         if (!join_is_removable(root, sjinfo))//判断是否可以清除连接
             continue;
 
         
         innerrelid = bms_singleton_member(sjinfo->min_righthand);
 
         remove_rel_from_query(root, innerrelid,
                               bms_union(sjinfo->min_lefthand,
                                         sjinfo->min_righthand));//从查询中删除相应的Rel
 
         
         nremoved = 0;
         joinlist = remove_rel_from_joinlist(joinlist, innerrelid, &nremoved);
         if (nremoved != 1)
             elog(ERROR, "failed to find relation %d in joinlist", innerrelid);
 
         
         //更新连接链表信息 
         root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);
 
         
         goto restart;
     }
 
     return joinlist;
 }

reduce_unique_semijoins
把可以简化的半连接转化为内连接.
比如以下的SQL语句:

select t1.*
from t_grxx t1 
where dwbh IN (select t2.dwbh from t_dwxx t2);

由于子查询"select t2.dwbh from t_dwxx t2"的dwbh是PK,子查询提升后,t_grxx的dwbh只对应t_dwxx唯一的一条记录,因此可以把半连接转换为内连接,执行计划如下:

testdb=# explain verbose select t1.*
from t_grxx t1 
where dwbh IN (select t2.dwbh from t_dwxx t2);
                                 QUERY PLAN                                  
-----------------------------------------------------------------------------
 Hash Join  (cost=1.07..20.10 rows=6 width=176)
   Output: t1.dwbh, t1.grbh, t1.xm, t1.xb, t1.nl
   Inner Unique: true
   Hash Cond: ((t1.dwbh)::text = (t2.dwbh)::text)
   ->  Seq Scan on public.t_grxx t1  (cost=0.00..14.00 rows=400 width=176)
         Output: t1.dwbh, t1.grbh, t1.xm, t1.xb, t1.nl
   ->  Hash  (cost=1.03..1.03 rows=3 width=38)
         Output: t2.dwbh
         ->  Seq Scan on public.t_dwxx t2  (cost=0.00..1.03 rows=3 width=38)
               Output: t2.dwbh
(10 rows)

跟踪分析:

(gdb) n
199   reduce_unique_semijoins(root);
(gdb) step
reduce_unique_semijoins (root=0x1702968) at analyzejoins.c:520
520   for (lc = list_head(root->join_info_list); lc != NULL; lc = next)
(gdb) n
522     SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);
(gdb)

查看SpecialJoinInfo内存结构:

528     next = lnext(lc);
(gdb) p *sjinfo
$1 = {type = T_SpecialJoinInfo, min_lefthand = 0x1749818, min_righthand = 0x1749830, syn_lefthand = 0x1749570, 
  syn_righthand = 0x17495d0, jointype = JOIN_SEMI, lhs_strict = true, delay_upper_joins = false, semi_can_btree = true, 
  semi_can_hash = true, semi_operators = 0x17496c8, semi_rhs_exprs = 0x17497b8}

内表(innerrel,即t_dwxx)如支持唯一性,则可以考虑把半连接转换为内连接

550     if (!rel_supports_distinctness(root, innerrel))
...
575     root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);
...

源代码如下:

 
 void
 reduce_unique_semijoins(PlannerInfo *root)
 {
     ListCell   *lc;
     ListCell   *next;
 
     
     for (lc = list_head(root->join_info_list); lc != NULL; lc = next)
     {
         SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);//特殊连接信息,先前通过deconstruct函数生成
         int         innerrelid;
         RelOptInfo *innerrel;
         Relids      joinrelids;
         List       *restrictlist;
 
         next = lnext(lc);
 
         
         if (sjinfo->jointype != JOIN_SEMI ||
             sjinfo->delay_upper_joins)
             continue;
 
         if (!bms_get_singleton_member(sjinfo->min_righthand, &innerrelid))
             continue;
 
         innerrel = find_base_rel(root, innerrelid);
 
         
         if (!rel_supports_distinctness(root, innerrel))
             continue;
 
         
         joinrelids = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);
 
         
         restrictlist =
             list_concat(generate_join_implied_equalities(root,
                                                          joinrelids,
                                                          sjinfo->min_lefthand,
                                                          innerrel),
                         innerrel->joininfo);
 
         
         if (!innerrel_is_unique(root,
                                 joinrelids, sjinfo->min_lefthand, innerrel,
                                 JOIN_SEMI, restrictlist, true))
             continue;
 
         
         root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);//删除特殊连接信息
     }
 }

add_placeholders_to_base_rels
把PHV分发到base rels中,代码较为简单

 
 void
 add_placeholders_to_base_rels(PlannerInfo *root)
 {
     ListCell   *lc;
 
     foreach(lc, root->placeholder_list)//遍历PH链表
     {
         PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
         Relids      eval_at = phinfo->ph_eval_at;
         int         varno;
 
         if (bms_get_singleton_member(eval_at, &varno) &&
             bms_nonempty_difference(phinfo->ph_needed, eval_at))//添加到需要的RelOptInfo中
         {
             RelOptInfo *rel = find_base_rel(root, varno);
 
             rel->reltarget->exprs = lappend(rel->reltarget->exprs,
                                             copyObject(phinfo->ph_var));
             
         }
     }
 }

到此,关于“PostgreSQL中remove_useless_joins的实现逻辑是怎样的”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

PostgreSQL中remove_useless_joins的实现逻辑是怎样的

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

目录