SQL做的能改成Oracle吗,从SQL改写到SQL重写,什么样的SQL才是好SQL?(黄浩)-程序员宅基地

技术标签: SQL做的能改成Oracle吗  

从SQL改写到SQL重写,什么样的SQL才是好SQL?黄浩 2016-12-14 10:02:26

作者介绍

黄浩,现任职于中国惠普,从业十年,始终专注于SQL。十年一剑,十年磨砺。3年通信行业,写就近3万条SQL;5年制造行业,遨游在ETL的浪潮;2年性能优化,厚积薄发自成一家。

在生活中,很多时候我们会有这样的体悟:问题要么不出,一旦出现,会像多诺米骨牌一样,会连锁引发诸多相关问题,让我们疲于应付。SQL优化也是如此,那厢,因一个视图代码变更引发的性能事件(参见案例:

1   涛声依旧,心有余悸

就在距离上次视图优化一个星期的时间,一封“红色”的邮件中的“SQL优化”项格外醒目:

thread-2073290-1-1.html

唯一值得庆幸的是,该性能问题被放在8月版本计划里面:时间还是蛮充裕的。于是我按照自己的节奏展开分析。

我找到开发责任人,拿到了SQL:

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

初一看,122行,不算长,也不短。但是我对SQL中出现的视图对象BAS_PROJECT_ALL_V产生了很大的兴趣。这个视图刚刚完成优化,怎么就又出现了性能问题了呢?看了下执行计划:

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

自上而下,体型虽然婀娜妙曼,但是“妙曼”得有些让人眩晕。这都要归功于视图对象BAS_PROJECT_ALL_V,该视图有5个union all,而在该SQL中,又被访问了3次。记得上次案例是由于谓词没有被推入引发的,而看执行计划,视图被访问三次,都没有做谓词推入,我就试着强制谓词推入看看效果,但是即便是谓词推入,问题依旧。因临近下班,也就没有深入分析,计划第二天再看看,反正离8月版本还有3周的时间。

2   友谊小船,说翻就翻

第二天一过来,因为有来自其他同事的性能问题,我暂时将昨天的性能问题搁在一旁。大概在11点钟的时候,托盘上的espace弹出了消息,发消息的是昨天提供SQL的同事,内容是:昨天的那个性能问题必须要在今天内完成优化。这个消息着实把我“震”住了,不是说好的是8月版本吗?友谊的小船咋说翻就翻呢?

原来,同样的性能问题在生产环境也出现了,而且生产用户直接提了一个BUG单,用户很生气,后果很严重。从测试人员到生产用户,从UAT到生产,从邮件到BUG单,事态的严重程度已完全超出了当下深圳高温天气的黄色预警级别,以至于我立马放下手头的工作,顶着烈烈炎日,大汗淋淋的赶往“事发现场”—开发责任人所在ODC。

因为越是时间紧迫,沟通就越显得紧要,何况昨天初步“目测”,一方面执行计划过于复杂,而代码逻辑似乎又并不简单,因此更需要当面沟通。

根据开发人员的描述,这个功能的业务需求很简单,如下:

根据登录用户ID,获取该用户对应的所有项目列表,用户的项目列表包含两部分:其一是分配至该用户下的项目列表,其二是该用户所在区域的所有初始化过的项目,如下图所示:

thread-2073290-1-1.html

在了解到业务需求后,我开始解读SQL,在解读的过程中不时的向开发人员请教、交流。约莫半小时后,SQL的逻辑框架也逐渐明朗起来,如下:

从逻辑示意图中,我们至少有如下两个疑问:

子查询route及T在配置项目列表和区域项目列表中都出现了,是否可以进行合并?

在“用户区域初始化项目列表”中,表SUROT_T与其他结果集没有任何关联条件,只有UID_C的过滤条件,这意味着会发生笛卡尔积。

了解完业务逻辑及SQL代码逻辑结构后,我们不能绕过视图BAS_PROJECT_ALL_V,且看该视图的代码逻辑:

thread-2073290-1-1.html

单从视图的数据逻辑看,存在以下问题:

表重复访问,图中黄色底纹和蓝色底纹的表对象都是重复访问;

除了T2结果集外,其他结果集都都访问了三个以上的表对象,模式都是一样的:以主表LEFT JOIN从表。众所周知,LEFT JOIN在SQL中的功能是获取字段,并没有过滤数据的作用。从LEFT JOIN的这些表获取的字段是否被外层的SQL访问了呢?如果没有被访问,那就意味着,就本案例的SQL而言,这些LEFT JOIN是多余的。

3   深入“虎穴”

带着上述分析后的问题,我深入分析了代码,确认了如下信息:

SQL中的部分代码是可以精简的,比如子查询ROUTE及T只是为了获取属性字段,完全可以在得到了所需的PN_C清单后,再与子查询ROUTE及T关联获取。

视图的代码也可以精简,比如在视图T5结果集中访问BPOPT_T表对象获取的字段,在SQL中根本没有访问,也就是说在T5结果集中完全可以不访问该表对象。

那么还有个问题,那就是笛卡尔积。而通过分析代码,发现并非没有关联条件,而是将关联条件写到了where过滤位置了,如下:

thread-2073290-1-1.html

基于该逻辑的复杂性,我决定将该过滤条件改写成EXISTS子查询。

4   大刀阔斧

业务需求了解了,大致的逻辑框架也清楚了,病症病因也定位了,接下来就是该大刀阔斧的进行SQL改写了,改写的过程就很简单了,改写后的SQL:

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

改动点如下:

取消视图BAS_PROJECT_ALL_V,用WITH 子查询替代视图。在with子查询中,根据需求关联表对象,砍掉了无关的表对象;

在获取区域项目列表的代码里,将LEFT JOIN WHERE改成EXISTS;

在获取了所有的项目列表后,再关联子查询ROUTE和T,获取项目相关属性信息。

修改后的执行计划如下:

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

在PL SQL DEV中执行,2.5S左右,看来SQL改写还是收到了成效,我也长长的舒了口气,看窗外,已是夕阳西下,一抹余晖透过玻璃窗投射进来,此刻在呼呼空调室内,丝丝暖意,却全然忘记早上发布的高温黄色预警,虽然已近黄昏,窗外依旧炙热。

5   分而治之,争分夺秒

第二天,正当我准备整理本次优化案例时,ESPACE弹出了消息,是开发人员发过来的。消息的内容让我立马停止了整理。因为开发人员告诉我,2.5S仍然不能满足需求,需要控制在2S内。此时此刻我的心情有些忐忑:根据经验,这0.5S的性能提升,其难度远比从10S优化到3S要大。

为了这0.5S,我又重新审视了昨天的优化方案。可以说昨天的优化方案已经对之前的代码结构做了很大程度的解构,但是更多的是“精简”SQL。在数据处理流程上还没有变化,优化前后的数据流程都是先集合再过滤,逻辑图如下:

thread-2073290-1-1.html

再看下执行计划:

thread-2073290-1-1.html

发现在UNION ALL六个来源的WITH子查询时,产生了31M的IO写操作,数据量达到了163K,而SQL最终返回的结果集不到1000条。此时,我看到了希望的曙光。我将数据处理流程方案做了优化,如下图所示:

thread-2073290-1-1.html

也就是说,将之前合而治之变更成了分而治之,在原理上显然是等价的。

根据分而治之的方案,改写后的SQL见附件:

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

thread-2073290-1-1.html

SQL改写后,怀着虔诚又激动的心情按下了F8,焦急得恍若停滞。1.8S,不容易呀。这0.7S的提升在动辄几秒钟、几分钟的优化空间中,如沧海一粟,但此刻却显得弥足珍贵。6   后记

该案例的优化过程其实就是一个SQL改写的过程,而最终演变成了SQL重写。这回到了一个最古老的问题:什么样的SQL才是好SQL?这个问题很难回答,因为这个问题跟哲学问题“什么样的人才是好人”是一样的。然而,我在长期与SQL为伴的过程中,从开发到优化,发现一个准则:简单即高效。这也合乎现在流行的返璞归真、大道至简的追求。

简单,并不是表现在代码量,而更在于SQL代码结构的简明、逻辑处理的简练。所在,在优化过程中,我首先考虑的是SQL自身的优化,也就是通常说的等价改写。我坚信,索引、Hint等技术的应用,应该是基于SQL已经极致化的假设。因为无论是索引,还是hint,在纷繁芜杂、不确定性的环境中,其负面影响也是巨大的。在风起云涌大浪淘沙时还能浪遏飞舟的,唯有“简单”的SQL。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_29378153/article/details/116296755

智能推荐

【爬虫实战】python文本分析库——Gensim-程序员宅基地

文章浏览阅读2.1k次,点赞25次,收藏37次。Gensim 允许你使用 TF-IDF 权重和其他算法来提取文档中的关键词。当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。_gensim

Android分享一张图片_android 分享一张图片到basequickadapter中-程序员宅基地

文章浏览阅读1k次。public class Act_Share extends Activity { private ShareCustomAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(_android 分享一张图片到basequickadapter中

Linux所有服务开放对应端口大全_linux服务器端口全部开放-程序员宅基地

文章浏览阅读4.7k次。Linux所有服务开放对应端口大全_linux服务器端口全部开放

php连接mysql实现简单注册登陆页面_php连接数据库登录和注册-程序员宅基地

文章浏览阅读1.6w次,点赞33次,收藏226次。PHP7连接数据库的方式:使用mysqli及PDOhttps://blog.csdn.net/zwliang98/article/details/82997349php输出执行sql语句的错误信息:mysqli_query($conn,$sql) or die(mysqli_error( $conn ));问题一:Incorrect integer value: ‘’ for colu..._php连接数据库登录和注册

深度学习之——防止过拟合的方法_加噪声防止过拟合-程序员宅基地

文章浏览阅读3.2k次,点赞8次,收藏13次。1、过拟合定义:在training data上的error渐渐减小,但是在验证集上的error却反而渐渐增大——因为训练出来的网络过拟合了训练集,对训练集外的数据却不work。模型越复杂,越容易过拟合。因此,原先以最小化损失(经验风险最小化)为目标:现在以最小化损失和模型复杂度(结构风险最小化)为目标:通过降低复杂模型的复杂度来防止过拟合的规则称为正则化。2、..._加噪声防止过拟合

Winform实现在DataGridView控件的单元格中添加多个控件_c# datagridview 添加多控件-程序员宅基地

文章浏览阅读3.6k次,点赞2次,收藏34次。Winform实现在DataGridView控件的单元格中添加多个控件背景实现思路关键代码背景DataGridView控件的列是支持TextBoxColumn、ComboBoxColumn等类型的,就是DataGridView的单元格进入编辑模式的时候就会出现对应的控件,但是有些业务场景是需要在一个单元格进入编辑状态时需要多个控件组合完成业务需求,就无法直接只用特定类型的Column来实现了。..._c# datagridview 添加多控件

随便推点

java通过ksoap调用_[Java教程]ksoap2-程序员宅基地

文章浏览阅读242次。[Java教程]ksoap20 2020-06-19 18:04:38 Android连接远程数据库,目前看来最好的解决办法就是webservice,利用webservice进行通讯就要soap协议,目前android没有内建相关的函数,需要借助第三方ksoap2-android进行操作。soap2-android官网地址https://simpligility.githu..._java ksoap2

MySQL中SELECT ... INTO的用法_select id into-程序员宅基地

文章浏览阅读2.7k次。MySQL中SELECT ... INTO的用法官方文档表述:1.SELECT ... INTO var_list selects column values and stores them into variables.2.SELECT ... INTO OUTFILE writes the selected rows to a file. Column and line termi_select id into

重装系统详细指南_电脑重装系统怎么操作csdn-程序员宅基地

文章浏览阅读7.4k次,点赞89次,收藏67次。电脑使用时间久了,无论是正常的系统升级,还是出了故障无法正常使用,都免不了要重装系统,本文详细地给大家介绍下如何重装系统,轻松完成。_电脑重装系统怎么操作csdn

出了国才能知道的十个小秘密_国国际海员-程序员宅基地

文章浏览阅读1.2k次。在国内时,了解“外面的世界”并不难,然而,认识的误区只有在国外住久了,慢慢地体会才能逐渐消除。下面十个方面如果不出国,我是不会知道很清楚的……   一、英语不努力学,是掌握不了的。 出国以前我以为到了国外,有了英语环境,自己会不费力地通过语言关。然而,除了小孩子,成年人欲消除语言障碍谈何容易?难怪许多在国外生活了半辈子的老侨,其英语程度还属初级。   二、从中国出口海外的食品,是给华人吃_国国际海员

git push error: 403_git push 403-程序员宅基地

文章浏览阅读5.0k次。我今天在把本地仓库push到远程仓库的时候,出现了问题:一会儿是这个:fatal: unable to access 'https://github.com/***/***/': OpenSSL SSL_read: Connection was reset, errno 10054一会儿又变成了这个:remote: Permission to Dxuan-chen/huashan.git denied to xuanfchen.fatal: unable to access 'https:_git push 403

excel shell合成_1分钟拆解:「如何将10多个工作表sheet,合并成一张?」-程序员宅基地

文章浏览阅读1.1k次。大家好,我是有讲课堂的认证达人:解题宝宝。今天到了VBA教学时间!因为今天阿,解题宝宝无聊闲逛,惊奇发现了两份VBA代码,特意分享给大家。是解决如何合并大量不同的工作表哒。多少张都没问题!亲测有效!分为以下两种情况☟01 合并同一工作簿的不同工作表。效果长这样:本来,同一工作簿下,一个排班表是一张sheet;接下里,就变成:所有排班表汇总成一张sheet,格式还自动排好!◎ 效果演示代码立即备上..._execl通过shell脚本的方式合并

推荐文章

热门文章

相关标签