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

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

原始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 谓词过滤条件)的更多相关文章
- SQL Server的优化器会缓存标量子查询结果集吗
在这篇博客"ORACLE当中自定义函数性优化浅析"中,我们介绍了通过标量子查询缓存来优化函数性能: 标量子查询缓存(scalar subquery caching)会通过缓存结果减 ...
- 反连接NOT EXISTS子查询中有or 谓词连接条件SQL优化一例
背景 今天在日常数据库检查中,发现一SQL运行时间特别长,于是抓取出来,进行优化. 优化前: 耗时:503s 返回:0 SQL代码 SELECT * FROM MM_PAYABLEMONEY_TD P ...
- SQL Server调优系列基础篇(子查询运算总结)
前言 前面我们的几篇文章介绍了一系列关于运算符的介绍,以及各个运算符的优化方式和技巧.其中涵盖:查看执行计划的方式.几种数据集常用的连接方式.联合运算符方式.并行运算符等一系列的我们常见的运算符.有兴 ...
- 优化有标量子查询的SQL
数据库环境:SQL SERVER 2008R2 今天在数据库中抓出一条比较耗费资源的SQL,只返回904条数据,居然跑了40多分钟.SQL及对应的数据量如下图: SELECT saft04.cur_y ...
- 标量子查询SQL改写
一网友说下面sql跑的好慢,让我看看 sql代码: select er, cid, pid, tbl, zs, sy, (select count(sr.mobile_tele_no) from tb ...
- SQL优化-标量子查询(数据仓库设计的隐患-标量子查询)
项目数据库集群出现了大规模节点宕机问题.经查询,问题在于几张表被锁.主要问题在于近期得几个项目在数据库SQL编写时大量使用了标量子查询. 为确定为题确实是由于数据表访问量超过单节点限制,做了一些测试. ...
- 标量子查询调优SQL
fxnjbmhkk4pp4 select /*+ leading (wb,sb,qw) */ 'blocker('||wb.holding_session||':'||sb.username||')- ...
- 在 SQL Server 数据库的 WHERE 语句中使用子查询
这是关于子查询语句的一系列文章中的第三篇.在这篇文章中我们将讨论WHERE语句中的子查询语句.其他的文章讨论了其他语句中的子查询语句. 本次课程中的所有例子都是基于Microsoft SQL Serv ...
- 数据库开发基础-SQl Server 主键、外键、子查询(嵌套查询)
主键 数据库主键是指表中一个列或列的组合,其值能唯一地标识表中的每一行.这样的一列或多列称为表的主键,通过它可强制表的实体完整性.当创建或更改表时可通过定义 PRIMARY KEY约束来创建主键.一个 ...
- SQL 必知必会·笔记<9>使用子查询
子查询(subquery),即嵌套在其他查询中的查询. 1. 利用子查询进行过滤 SELECT 语句中,子查询总是从内向外处理.示例: SELECT cust_name, cust_contact F ...
随机推荐
- linux 脚本:iptables-secure.sh
#!/bin/bash # 2022.2.28 by dewan # secutiry configuration. usage () { echo "$0 start # setup se ...
- 2021-7-6 Vue实现记事本功能
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script sr ...
- linux文本编辑YCM报错
linux文本编辑YCM报错 刚从github安装了vimplus,可是发现存在不少的问题.索性给直接记录一下. The ycmd server SHUT DOWN (restart with ':Y ...
- 2023-07-29:给你一个由数字组成的字符串 s,返回 s 中独特子字符串数量。 其中的每一个数字出现的频率都相同。
2023-07-29:给你一个由数字组成的字符串 s,返回 s 中独特子字符串数量. 其中的每一个数字出现的频率都相同. 答案2023-07-29: 大体步骤如下: 1.初始化变量base为固定值10 ...
- python2.7源码安装方式
安装python2.7 下载Python 2.7, 下载地址 解压安装 tar -xzvf Python-2.7.15.tgz cd Python-2.7.15 ./configure --prefi ...
- Java日志系列:日志门面JCL、SLF4J
目录 一.日志门面说明 二.JCL 使用 JCL 入门 JCL 原理 三.SLF4J 使用 配合自身简单日志实现(slf4j-simple) 配置logback日志实现 配置Log4J日志实现(需适配 ...
- 解放双手!ChatGPT助力编写JAVA框架
亲爱的Javaer们,在平时编码的过程中,你是否曾想过编写一个Java框架去为开发提效?但是要么编写框架时感觉无从下手,不知道从哪开始.要么有思路了后对某个功能实现的技术细节不了解,空有想法而无法实现 ...
- vscode 中 Markdown 粘贴图片的位置
背景 自从 typora 开始收费后, 不少人开始寻找其他的 Markdown编辑器, 我觉得 vscode 就是一个很不错的选择 虽然不能像 typora 在Markdown预览中编辑, 但是左右布 ...
- 零代码基础,一分钟教你快速搭建微信ChatGPT机器人!
本教程收集于:ChatGPT聊天机器人搭建全攻略汇总:精心整理 Github登录账号后,先Forck下仓库:https://github.com/zhayujie/chatgpt-on-wechat ...
- js调起android安卓与ios苹果方法 vue3.0 + ts
let shareSelect = (ev :any) => { const u :any= navigator.userAgent; const win :any = window const ...