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

题目内容如下:

原始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. Powe AutoMate:列表操作

    大纲 记录对列表的操作 创建列表 向列表中添加元素 添加多个 合并列表 运行结果 反转列表 反转前 反转后 删除列表中的重复项 结果: 减去列表 结果:

  2. IDEA:使用Test注解,控制台无法输入

    解决方案 步骤一: 点击help ===> Edit Custom VM Options... 步骤二: 添加文件末尾添加如下内容 -Deditable.java.test.console=tr ...

  3. gitlab-runner-config-in-docker

    gitlab in docker 网上有很多现成的解决方案,本文仅作流程梳理,若不需要,可直接用gitlab官方提供的镜像 installation Dockerfile FROM registry. ...

  4. Linux下发现一个高安全性的系统管理工具

    软件 AnySetup 主要功能 主要功能是对Linux操作系统下的基本配置进行管理.多种服务配置进行管理.安全配置进行管理等.如:操作系统的升级管理,软件包的安装.更新和卸载管理,软件仓库源的管理, ...

  5. quarkus依赖注入之十二:禁用类级别拦截器

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<quarkus依赖注入> ...

  6. 2023牛客暑期多校训练营7 CGILM

    比赛链接 C 题解 知识点:位运算,贪心. 我们用分段的思想考虑大小关系,若在同一段则大小不能确定,一开始为 \([1,n]\) . 我们按位从高到低考虑,某位如果 \(b_i\) 产生了 \(1\) ...

  7. do_fork(一)

    fork 是linux创建进程的系统调用,相关的函数(不只是系统调用)还有 vfork,clone,sys_frok等.这些函数会整理不同参数,再调用到 do_fork 中. 本篇文章主要介绍do_f ...

  8. 使用阿里云ECS和RDS搭建个人博客

    一.ECS实例配置 1.重置云服务器ECS密码 前往ECS控制台,点击实例,找到刚才开通的ECS实例(找不到的话就看一下上方的地区是否是你的服务器的地域),点击右侧操作栏中的三个点,找到重置实例密码, ...

  9. 获取API接口返回的商品详情数据后该如何使用

    获取API接口返回的商品详情数据后,我们可以使用以下方式将其处理和利用: 数据展示:我们可以将API接口返回的商品详情数据以列表.表格.图形等形式展示给用户,以便他们更好地了解商品的基本信息.特征.评 ...

  10. Code Llama:Llama 2 学会写代码了!

    引言 Code Llama 是为代码类任务而生的一组最先进的.开放的 Llama 2 模型,我们很高兴能将其集成入 Hugging Face 生态系统!Code Llama 使用与 Llama 2 相 ...