同事今天晋级高级工程师考试,发来一道公司出题目让我帮忙进行优化,其中场景二的案例非常有意思。

题目内容如下:

原始SQL:
scott=> explain analyze
scott-> select
scott-> a.id,
scott-> a.col2,
scott-> (select sum(b.id) from table02 b where a.col2 like b.col2||'%' )
scott-> from table01 a; QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Seq Scan on table01 a (cost=0.00..3905341.00 rows=100000 width=45) (actual time=0.579..50568.090 rows=100000 loops=1)
SubPlan 1
-> Aggregate (cost=39.02..39.03 rows=1 width=8) (actual time=0.504..0.504 rows=1 loops=100000)
-> Seq Scan on table02 b (cost=0.00..39.00 rows=10 width=4) (actual time=0.063..0.499 rows=8 loops=100000)
Filter: (a.col2 ~~ (col2 || '%'::text))
Rows Removed by Filter: 1992
Planning Time: 0.097 ms
Execution Time: 50590.882 ms
(8 行记录) 时间:50591.756 ms (00:50.592)

table01、table02 这两张表没有创建任何索引,全表扫描+标量子查询SQL执行需要50s才能出结果,速度非常慢。

考题要求要优化这条SQL,意思既是无论是调整 postgresql数据库的参数,对SQL加索引,等价改写SQL,这些手段都没问题,只要能让执行速度变快就行。

由于当时我在忙其他的事情,大致看了一眼后给出了以下的改写方案(我没加索引,感觉加索引的用处不大):

改写1:

scott=> explain analyze select
scott-> a.id,
scott-> a.col2,
scott-> b.sum_b_id
scott-> from table01 a
scott-> left join (select sum(b.id) sum_b_id,b.col2 from table02 b group by b.col2) b
scott-> ON a.col2 like b.col2||'%'
scott-> ; QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Nested Loop Left Join (cost=39.00..448135.74 rows=127500 width=45) (actual time=1.283..8674.517 rows=100000 loops=1)
Join Filter: (a.col2 ~~ (b.col2 || '%'::text))
Rows Removed by Join Filter: 25400387
-> Seq Scan on table01 a (cost=0.00..1841.00 rows=100000 width=37) (actual time=0.018..19.620 rows=100000 loops=1)
-> Materialize (cost=39.00..45.37 rows=255 width=11) (actual time=0.000..0.024 rows=255 loops=100000)
-> HashAggregate (cost=39.00..41.55 rows=255 width=11) (actual time=1.241..1.316 rows=255 loops=1)
Group Key: b.col2
-> Seq Scan on table02 b (cost=0.00..29.00 rows=2000 width=7) (actual time=0.007..0.342 rows=2000 loops=1)
Planning Time: 0.181 ms
Execution Time: 8682.974 ms
(10 行记录) 时间:8684.338 ms (00:08.684)

可以看到等价改写以后,SQL从原来执行 50s 的时间已经降低到 8.8s 左右,提升还是挺大的。

把答案给了同事,我也去忙其他的事情了。

晚上我闲下来没事做,贼无聊,仔细看了下改写1 SQL的执行计划,感觉这种计划可能不是最优的执行计划。

因为我始终感觉走HASH可能才是最佳的执行计划,如果这条SQL在ORACLE 上执行,CBO很大可能会让计划走HASH,但是在PG就是走NL(脑残优化器)。

吃完饭后一直在尝试改写,搞了哥很长时间,最终还是把HASH版本的SQL给改出来了,泪目。

改写2:

scott=> explain analyze
scott-> with a as (select id,col2,substr(t1.col2,1,x.rn) rn1 from table01 t1,
scott(> scott(> (select min(length(col2)) rn from table02) x),
<t2.col2,1,x.rn) rn2 from (select sum(b.id) sum_b_id,b.col2 from table02 b group by b.col2) t2,
scott(> (select min(length(col2)) rn from table02) x)
scott-> select a.id,a.col2,b.sum_b_id from a
scott-> left join b on a.rn1 = b.rn2 and a.col2 like b.col2||'%';
QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------
--------
Hash Left Join (cost=127.86..14881.38 rows=100000 width=45) (actual time=2.322..215.695 rows=100000 loops=1)
Hash Cond: (substr(t1.col2, 1, (min(length(table02.col2)))) = substr(b.col2, 1, (min(length(table02_1.col2)))))
Join Filter: (t1.col2 ~~ (b.col2 || '%'::text))
-> Nested Loop (cost=39.00..2880.02 rows=100000 width=41) (actual time=0.588..36.635 rows=100000 loops=1)
-> Aggregate (cost=39.00..39.01 rows=1 width=4) (actual time=0.580..0.582 rows=1 loops=1)
-> Seq Scan on table02 (cost=0.00..29.00 rows=2000 width=3) (actual time=0.015..0.252 rows=2000 loops=1)
-> Seq Scan on table01 t1 (cost=0.00..1841.00 rows=100000 width=37) (actual time=0.005..15.073 rows=100000 loops=1)
-> Hash (cost=85.67..85.67 rows=255 width=15) (actual time=1.721..1.724 rows=255 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 20kB
-> Nested Loop (cost=78.00..85.67 rows=255 width=15) (actual time=1.500..1.602 rows=255 loops=1)
-> Aggregate (cost=39.00..39.01 rows=1 width=4) (actual time=0.554..0.555 rows=1 loops=1)
-> Seq Scan on table02 table02_1 (cost=0.00..29.00 rows=2000 width=3) (actual time=0.004..0.233 rows=2000 l
oops=1)
-> HashAggregate (cost=39.00..41.55 rows=255 width=11) (actual time=0.945..1.002 rows=255 loops=1)
Group Key: b.col2
-> Seq Scan on table02 b (cost=0.00..29.00 rows=2000 width=7) (actual time=0.005..0.250 rows=2000 loops=1)
Planning Time: 0.351 ms
Execution Time: 224.017 ms
(17 行记录) 时间:225.488 ms

这个案例从最早的 50秒 改写到 8秒,到最后的 225毫秒出结果,花了不少时间研究各种改写方式。

只能说PG的优化器确实太拉跨了,浪费开发者不少时间,换成ORACLE数据库不会走这种SB执行计划。

以后估计会很少发博客,目前正在考虑转行卖炒粉,目前市场真的是一言难尽。

分享一次公司晋级考试的SQL题目,非常有趣的案例(postgresql 标量子查询 where lie 谓词过滤条件)的更多相关文章

  1. SQL Server的优化器会缓存标量子查询结果集吗

    在这篇博客"ORACLE当中自定义函数性优化浅析"中,我们介绍了通过标量子查询缓存来优化函数性能: 标量子查询缓存(scalar subquery caching)会通过缓存结果减 ...

  2. 反连接NOT EXISTS子查询中有or 谓词连接条件SQL优化一例

    背景 今天在日常数据库检查中,发现一SQL运行时间特别长,于是抓取出来,进行优化. 优化前: 耗时:503s 返回:0 SQL代码 SELECT * FROM MM_PAYABLEMONEY_TD P ...

  3. SQL Server调优系列基础篇(子查询运算总结)

    前言 前面我们的几篇文章介绍了一系列关于运算符的介绍,以及各个运算符的优化方式和技巧.其中涵盖:查看执行计划的方式.几种数据集常用的连接方式.联合运算符方式.并行运算符等一系列的我们常见的运算符.有兴 ...

  4. 优化有标量子查询的SQL

    数据库环境:SQL SERVER 2008R2 今天在数据库中抓出一条比较耗费资源的SQL,只返回904条数据,居然跑了40多分钟.SQL及对应的数据量如下图: SELECT saft04.cur_y ...

  5. 标量子查询SQL改写

    一网友说下面sql跑的好慢,让我看看 sql代码: select er, cid, pid, tbl, zs, sy, (select count(sr.mobile_tele_no) from tb ...

  6. SQL优化-标量子查询(数据仓库设计的隐患-标量子查询)

    项目数据库集群出现了大规模节点宕机问题.经查询,问题在于几张表被锁.主要问题在于近期得几个项目在数据库SQL编写时大量使用了标量子查询. 为确定为题确实是由于数据表访问量超过单节点限制,做了一些测试. ...

  7. 标量子查询调优SQL

    fxnjbmhkk4pp4 select /*+ leading (wb,sb,qw) */ 'blocker('||wb.holding_session||':'||sb.username||')- ...

  8. 在 SQL Server 数据库的 WHERE 语句中使用子查询

    这是关于子查询语句的一系列文章中的第三篇.在这篇文章中我们将讨论WHERE语句中的子查询语句.其他的文章讨论了其他语句中的子查询语句. 本次课程中的所有例子都是基于Microsoft SQL Serv ...

  9. 数据库开发基础-SQl Server 主键、外键、子查询(嵌套查询)

    主键 数据库主键是指表中一个列或列的组合,其值能唯一地标识表中的每一行.这样的一列或多列称为表的主键,通过它可强制表的实体完整性.当创建或更改表时可通过定义 PRIMARY KEY约束来创建主键.一个 ...

  10. SQL 必知必会·笔记<9>使用子查询

    子查询(subquery),即嵌套在其他查询中的查询. 1. 利用子查询进行过滤 SELECT 语句中,子查询总是从内向外处理.示例: SELECT cust_name, cust_contact F ...

随机推荐

  1. msvc++工程之vs版本升级及工程目录规范

    为什么要升级msvc++工程版本 对msvc++工程进行vs版本升级,一方面是可以使用较新的C++标准及对64位更好的支持. 首先你需要对msvc++ project文件有一定的了解,主要是vcxpr ...

  2. croc-文件传输工具

    前言 croc是一款用go语言开发的命令行文件传输工具,该工具允许两台计算机设备以一种简单和安全的方式来传输文件. GitHub项目地址 环境信息 IP 系统版本 croc版本 说明 192.168. ...

  3. [clickhouse]同步MySQL

    前言 clickhouse的查询速度非常快,而且兼容大部分MySQL的sql语法,因此一般将clickhouse作为MySQL的读库. 本文提供两种clickhouse同步MySQL的方式 click ...

  4. 微信的 h5 支付和 jsapi 支付

    目录 申请商户号 申请商户证书 设置APIv3密钥 下载 SDK 开发包 下载平台证书 关联 AppID 账号 开通 H5 支付 H5支付流程 开通 JSAPI 支付 JSAPI 支付流程 通用微信支 ...

  5. IDA函数特征识别自动签名

    IDA函数特征识别自动签名 Vc6编译的有些无法识别一些库里面的函数 测试代码 #include <stdio.h> int main() { printf("123456\n& ...

  6. Hugging News #0821: 新的里程碑:一百万个代码仓库!

    每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...

  7. Solution -「CCPC Winter Camp Day 6 A」Convolution

    Description Link. 给定一个数列 \(\sf a_1,a_2,....a_n\),请求出下面这个结果在模 \(\sf 998244353\) 下的答案. \[\sum_{i=1}^{n ...

  8. CAP项目集成带身份和证书验证的MongoDB

    大家好,我是Edison. 最近,在使用CAP事件总线时,碰到了这样一个需求:微服务采用的是MongoDB,而且还是带身份验证 和 SSL根证书验证的.由于目前网上能找到的资料,都是不带身份验证的Mo ...

  9. .NET开发工作效率提升利器 - CodeGeeX AI编程助手

    前言 2022年6月,随着GitHub Copliot正式面向大众发布.让许多开发者都感受到了AI辅助编程工具的魅力所在,Copilot实现了帮助开发者大大提高了编程开发效率,让程序员朝九晚五成为可能 ...

  10. 基本环境安装 jdk,mq,redis,nginx

    JDK:解压安装包,命令为 tar -zxvf jdk-8u381-linux-x64.tar.gz配置环境变量,使用 vim 命令(需要安装vim,安装命令为:yum install vim)修改 ...