当前位置: 首页 > news >正文

SQL改写:99%DBA估计都会忽略的重大知识点

我们的文章会在微信公众号IT民工的龙马人生和博客网站( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢!
由于博客中有大量代码,通过页面浏览效果更佳。

今天在给一个网友优化一条含有子查询的SQL时,用到了子查询转外连接的改写技术,被网友质疑改写where过滤行有问题。通过了解后,原来是网友对Where过滤行的理解有分歧导致的。这个分歧就是Where条件过滤行到底是对关联后的结果集进行过滤还是对关联前表上的行进行过滤呢?这个问题相信大部分DBA可能都会忽略的重大知识点,特别是原来在传统的Oracle纯运维的DBA尤为突出。今天就针对网友理解的这个知识点在PG环境中演示一下,通过执行计划来演示和说明一下。

1,结论

这里把结论放在前面,感兴趣的可以直接看结论,节约大家的时间。

  • 等值连接中where过滤条件是过滤表中的记录。
  • 在外连接中where过滤被关联表的列的IS NULL过滤时是对关联后的结果集进行过滤,其它是对过滤表中的记录。

2,创建测试表

这里就直接利用pg_class来创建两张测试表。

htz=# create table source as select * from pg_class;
SELECT 480
htz=# create table target as select *from pg_class;
SELECT 483

3,等值连接

3.1 模拟需求

查询source中relname为account,并且oid在target中存在的记录

htz=# explain analyze  select count(*) from source a join target b using(oid) where a.relname='account';QUERY PLAN                                                      
---------------------------------------------------------------------------------------------------------------------Aggregate  (cost=36.67..36.68 rows=1 width=8) (actual time=0.164..0.165 rows=1 loops=1)->  Hash Join  (cost=18.01..36.66 rows=1 width=0) (actual time=0.083..0.160 rows=1 loops=1)Hash Cond: (b.oid = a.oid)->  Seq Scan on target b  (cost=0.00..16.83 rows=483 width=4) (actual time=0.011..0.055 rows=483 loops=1)->  Hash  (cost=18.00..18.00 rows=1 width=4) (actual time=0.062..0.062 rows=1 loops=1)Buckets: 1024  Batches: 1  Memory Usage: 9kB->  Seq Scan on source a  (cost=0.00..18.00 rows=1 width=4) (actual time=0.008..0.059 rows=1 loops=1)Filter: (relname = 'account'::name)Rows Removed by Filter: 479Planning Time: 0.113 msExecution Time: 0.229 ms
(11 rows)

其实上面的SQL可以等价改写为:

explain analyze  select count(*) from (select * from source where relname='account') a join target b using(oid);

执行计划如下:

htz=# explain analyze  select count(*) from (select * from source where relname='account') a join target b using(oid);QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------Aggregate  (cost=36.67..36.68 rows=1 width=8) (actual time=0.586..0.588 rows=1 loops=1)->  Hash Join  (cost=18.01..36.66 rows=1 width=0) (actual time=0.345..0.582 rows=1 loops=1)Hash Cond: (b.oid = source.oid)->  Seq Scan on target b  (cost=0.00..16.83 rows=483 width=4) (actual time=0.062..0.264 rows=483 loops=1)->  Hash  (cost=18.00..18.00 rows=1 width=4) (actual time=0.256..0.257 rows=1 loops=1)Buckets: 1024  Batches: 1  Memory Usage: 9kB->  Seq Scan on source  (cost=0.00..18.00 rows=1 width=4) (actual time=0.026..0.234 rows=1 loops=1)Filter: (relname = 'account'::name)Rows Removed by Filter: 479Planning Time: 2.100 msExecution Time: 0.778 ms
(11 rows)

这里注意关键行的信息:

               ->  Seq Scan on source  (cost=0.00..18.00 rows=1 width=4) (actual time=0.026..0.234 rows=1 loops=1)Filter: (relname = 'account'::name)Rows Removed by Filter: 479

这里说明在source全表扫描,扫描完成后通过relname='account'来筛选满足条件的行,其中不满足条件的行被Filter删除了479行。

3.2 等值查询的结论

所以在等值查询中Where后面的过滤条件是直接过滤表的行。

4,外连接情况

外连接会涉及到2张表的关联,where中对2张表的过滤会带来完全不同的效果。这里我们对外连接中需要保留记录的表叫主库表,另外一张表就叫被连接表,比如在a left join b,这a就是主表,b就是被连接表;a right join b,b就是主表,a就是被连接表。where中对被驱动表中的列进行is null判断时,是对关联后的结果集进行过滤,对其它情况的过滤跟上面等值连接是一样的效果。

4.1 模拟非is null过滤需求

这里模拟一个需求就是查询target中relname为account的记录,利用oid对source进行关联,如果在source中存在,就展示source中的信息,如果不存在source返回null记录。

select * from target a  left join source b using(oid) where a.relname='account'; 

执行计划的信息如下:

htz=# explain analyze select * from target a  left join source b using(oid) where a.relname='account'; QUERY PLAN                                                    
-----------------------------------------------------------------------------------------------------------------Hash Right Join  (cost=18.05..36.66 rows=1 width=526) (actual time=0.340..0.557 rows=1 loops=1)Hash Cond: (b.oid = a.oid)->  Seq Scan on source b  (cost=0.00..16.80 rows=480 width=265) (actual time=0.015..0.179 rows=480 loops=1)->  Hash  (cost=18.04..18.04 rows=1 width=265) (actual time=0.285..0.285 rows=1 loops=1)Buckets: 1024  Batches: 1  Memory Usage: 9kB->  Seq Scan on target a  (cost=0.00..18.04 rows=1 width=265) (actual time=0.074..0.266 rows=1 loops=1)Filter: (relname = 'account'::name)Rows Removed by Filter: 482Planning Time: 0.444 msExecution Time: 0.737 ms
(10 rows)

结果跟等值连接是一样的。

4.2 结果

结论跟等值连接一样

4.3

这里模拟一个需求就是查询target中存在oid记录,如果oid在source中不存在。

select * from target a  left join source b using(oid) where b.oid is null;

对应的执行计划如下:

htz=# explain analyze  select * from target a  left join source b using(oid) where b.oid is null;QUERY PLAN                                                      
---------------------------------------------------------------------------------------------------------------------Hash Anti Join  (cost=22.80..41.47 rows=3 width=526) (actual time=1.273..1.399 rows=3 loops=1)Hash Cond: (a.oid = b.oid)->  Seq Scan on target a  (cost=0.00..16.83 rows=483 width=265) (actual time=0.025..0.114 rows=483 loops=1)->  Hash  (cost=16.80..16.80 rows=480 width=265) (actual time=1.021..1.021 rows=480 loops=1)Buckets: 1024  Batches: 1  Memory Usage: 102kB->  Seq Scan on source b  (cost=0.00..16.80 rows=480 width=265) (actual time=0.046..0.508 rows=480 loops=1)Planning Time: 0.305 msExecution Time: 1.650 ms

这里注意几个关键行的信息:

----------------------------------------Hash Anti Join  (cost=22.80..41.47 rows=3 width=526) (actual time=1.273..1.399 rows=3 loops=1)

优化器将SQL语句重新为反连接(not in /not exists),这个地方的where b.oid is null就是对整个结果集的过滤。上面的SQL语句其实可以重写为下面SQL:

 select * from target a where  not exists ( select * from source b where a.oid=b.oid);

改写后的执行计划如下:

htz=# explain analyze  select * from target a where  not exists ( select * from source b where a.oid=b.oid);QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------Hash Anti Join  (cost=22.80..41.47 rows=3 width=265) (actual time=0.111..0.132 rows=3 loops=1)Hash Cond: (a.oid = b.oid)->  Seq Scan on target a  (cost=0.00..16.83 rows=483 width=265) (actual time=0.007..0.032 rows=483 loops=1)->  Hash  (cost=16.80..16.80 rows=480 width=4) (actual time=0.064..0.065 rows=480 loops=1)Buckets: 1024  Batches: 1  Memory Usage: 25kB->  Seq Scan on source b  (cost=0.00..16.80 rows=480 width=4) (actual time=0.003..0.037 rows=480 loops=1)Planning Time: 0.126 msExecution Time: 0.221 ms
(8 rows)

4.4 结论

在外连接中对被连接列进行IS NULL过滤时是对关联后的结果集进行判断和过滤。

4 总结论

关于整个实验的结论,可以参考第一部分,这里简单写一下关于怎么快速的进行上面判断,只需要看执行计划是否存在表上面有Filter过滤即可。

------------------作者介绍-----------------------
姓名:黄廷忠
个人博客: (http://www.htz.pw)
CSDN地址: (https://blog.csdn.net/wwwhtzpw)
博客园地址: (https://www.cnblogs.com/www-htz-pw)

http://www.jsqmd.com/news/24784/

相关文章:

  • NAS助手 — 纯血鸿蒙时代的 NAS 文件分享新方案
  • 2025年办公室玻璃隔断型材厂家权威推荐榜单:专业玻璃隔断/广州办公室隔断/双层玻璃百叶隔断源头厂家精选
  • Draco 编译及配置
  • 第十一届中国大学生程序设计竞赛 女生专场
  • JAVA 对于class文件反编译,CFR 通常能产生最完整的代码。
  • 2025全焊接换热器/板式换热器/清洗维修推荐榜:科睿泽换热(苏州)领跑,四大企业以高效传热赋能工业节能
  • 2025橡胶/变形缝中埋式/中置式橡胶/预埋式橡胶/内埋式/止水带推荐榜:众航防水领跑衡水市场,四大企业以技术筑牢工程防渗防线
  • 2025 浸没式/全/液冷超充推荐榜:中碳创新领跑 “超充之城”,四大企业解锁低碳补能新范式
  • 【运维自动化-标准运维】各类全局变量使用说明-元变量(完结)
  • 2025年不变色二氧化硅厂家权威推荐榜单:通用型二氧化硅/可定制二氧化硅/高吸油二氧化硅源头厂家精选
  • linux系统启动卡在(1 of 2) A start job is running for .... ()
  • MES 他山之石:红日药业MES 文摘
  • HDMI辐射整改案例-阿赛姆电子
  • 关于curl-一个网址-报错-OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to
  • 电脑截图怎么往下拖着截图?4种方法教你轻松实现长页面截图(滚动截图超全教程)
  • 2025年云南旅游租车公司最新服务推荐榜:芒市旅游包车/芒市旅游租车/云南旅游包车/专注体验与可靠,打造无忧旅途
  • 102302125 苏琎宇 数据采集第1次作业
  • 哈希优化策略
  • 一站式开发速查表大全 - 覆盖主流编程语言与工具
  • GNU C和ANSI C的一些差异
  • gcc系编译器、调试器的应用和c/c++编译原理
  • JAVA FX初次使用并制作辅助工具指南
  • Day6综合案例1-体育新闻列表
  • 题解:AT_agc015_e [AGC015E] Mr.Aoki Incubator
  • SNP特征通道数是什么意思
  • CF1482E Skyline Photo
  • sqlserver 添加或修改字段
  • 最小瓶颈生成树
  • 小程序语音通话让智能设备会“说话”
  • 易基因: NG (IF29):颠覆认知!深圳仙湖植物园刘阳团队WGBS及超级泛基因组分析揭示苔藓植物基因家族比维管植物更丰富|项目文章