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

题目内容如下:

原始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. java 线程等待和唤醒方法

    java线程状态变迁图 从图中可以看出Java 线程等待方法是将线程从Runnable状态转换为Waiting状态,Java线程的唤醒方法是将线程从Waiting状态唤醒进入Runnable状态 在J ...

  2. 2022-1-11 控件学习4 ItemControl、ListBox、ComboBox

    ItemControl itemControl前台 ItemControl后台 ItemControl一般是竖直排列的,如果需要很想排列需要使用,也可以使用 UniformGrid Columns=& ...

  3. 2021-7-7 Vue的for使用实例

    <!DOCTYPE html> <html> <head> <title> </title> </head> <body& ...

  4. 关于 Task 简单梳理(C#)【并发编程系列】

    〇.前言 Task 是微软在 .Net 4.0 时代推出来的,也是微软极力推荐的一种多线程的处理方式. 在 Task 之前有一个高效多线程操作类 ThreadPool,虽然线程池相对于 Thread, ...

  5. 【技术积累】Linux中的命令行【理论篇】【四】

    ar命令 命令介绍 ar命令是Linux系统中的一个工具,用于创建.修改和提取静态库文件(archive files).静态库文件是一组已编译的目标文件的集合,可以被链接到可执行文件中. 命令说明 a ...

  6. debian11编译安装freeswitch

    前言 环境: 系统版本:debian 11 x86_64 FreeSWITCH版本:1.10.6 安装步骤 安装依赖(安装之前最好换apt软件源为国内的) apt install -y gnupg2 ...

  7. 工具—批量备案信息查询并生成fofa查询语句

    描述: 1.可以输入一个或多个公司名或域名或备案号,得到备案信息(备案公司名,备案公司网站url,备案号,域名类型,审核时间) 2.读取生成的信息并转为fofa语句,方便了指定目标的信息收集速度 工具 ...

  8. [ABC142E] Get Everything

    2023-02-18 题目 题目传送门 翻译 翻译 难度&重要性(1~10):5 题目来源 AtCoder 题目算法 状压dp 解题思路 我们令 \(S\) 表示当前箱子状态,\(P_i\) ...

  9. GAN!生成对抗网络GAN全维度介绍与实战

    本文为生成对抗网络GAN的研究者和实践者提供全面.深入和实用的指导.通过本文的理论解释和实际操作指南,读者能够掌握GAN的核心概念,理解其工作原理,学会设计和训练自己的GAN模型,并能够对结果进行有效 ...

  10. ES集群&kibana安装

    一.elasticsearch介绍 Elasticsearch 是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful 风格接口,多数据源,自动搜索负 ...