今天同事咨询一个SQL语句,如下所示,SQL语句本身并不复杂,但是执行效率非常糟糕,糟糕到一塌糊涂(执行计划也是相当复杂)。如果查询条件中没有NOT EXISTS部分,倒是不要一秒就能查询出来。

SELECT * FROM dbo.UVW_PDATest a WITH(NOLOCK)

WHERE  

Remark='前纺' AND Operation_Name='粗纱' AND One_Status_Code='0047'  

         AND a.Createtime >='2015-9-23'

AND  NOT EXISTS

        (

          SELECT 1 FROM dbo.UVW_PDATest c WITH(NOLOCK)

          WHERE a.Task_NO =c.Task_NO AND c.One_Status_Code='0014'

        )

为什么如此简单的SQL语句,执行效率却一塌糊涂呢,因为UVW_PDATest是一个视图,而且该视图是由8个表关联组成。

SELECT ..........

From dbo.PDA_TB_Produce a With(Nolock)

  Join dbo.DctOperationList b With(Nolock)

   On a.Operation_Code=b.Operation_Code

  Join dbo.DctOneStatusList c With(Nolock)

   On a.One_Status_Code=c.One_Status_Code

  Left join dbo.DctTwoStatusList d With(Nolock)

   On c.One_Status_Code=d.One_Status_Code and a.Two_Status_Code=d.Two_Status_Code

  left Join dbo.DctMachineList e With(Nolock)

   On a.Operation_Code=e.Operation_Code and a.Machine_Code=e.Machine_Code

  left Join dbo.DctOperationList f With(Nolock)

   On a.Next_Operation_Code=f.Operation_Code

  Join dbo.DctUserList g With(Nolock)

   On a.User_ID_Operating=g.User_ID

  Join dbo.DctUserList h With(Nolock)

   On a.User_ID=h.User_ID

刚开始我想从索引上去优化,加上一两个索引后发现其实并无多大益处。为什么性能会如此糟糕呢?原因是什么呢?

大量复杂的Join

该类查询模式包含了大量连接,尤其是连接条件是不等连接,由于统计信息随着表连接的增多精度逐渐下降,这会导致低效的查询性能。解决这类情况可以通过分解查询,并将中间解决存入临时表解决。 具体参考博客:什么情况下应该分解复杂的查询来提升性能

于是我拆分上面SQL语句(如下所示),先将执行结果保存到临时表,然后关联取数,结果一秒钟的样子就执行出来了。真可谓是化繁为简。

SELECT Task_NO INTO #TMP_MID_UVW_PDATest

FROM dbo.UVW_PDATest c WITH(NOLOCK)

  WHERE One_Status_Code='0014' and Remark='前纺' AND Operation_Name='粗纱'

         

SELECT * INTO #TMP_UVW_PDATest

FROM dbo.UVW_PDATest a WITH(NOLOCK)

WHERE   Remark='前纺'

 AND Operation_Name='粗纱'

 AND One_Status_Code='0047'  

    AND Create_Date>='2015-9-23' ;

         

SELECT  * FROM #TMP_UVW_PDATest a

    WHERE NOT EXISTS(SELECT 1 FROM #TMP_MID_UVW_PDATest c WHERE a.Task_NO =c.Task_NO );

 

DROPTABLE#TMP_UVW_PDATest

DROP TABLE #TMP_MID_UVW_PDATest

第二个案例是ORACLE数据库的一个优化案例,具体SQL语句如下所示,执行时间非常长,一般都是二十多秒左右。

SELECT A.SC_NO,

   A.MRP_GROUP_CD, 

   A.DIMM_ID, 

   A.JOB_ORDER_NO, 

   DECODE(SIGN(A.DEMAND_QTY),-1,0,A.DEMAND_QTY) AS DIFF_QTY, 

   A.ASSIGNED_TYPE 

FROM 

   (

   SELECT CC.SC_NO,

      BB.MRP_GROUP_CD, 

      BB.DIMM_ID, 

      BB.JOB_ORDER_NO, 

      NVL (SUM (BB.DEMAND_QTY), 0) - NVL(SUM(REC.RECV_QTY),0) AS DEMAND_QTY, 

      CASE 

         WHEN DD.REQ_DATE<TRUNC(SYSDATE) THEN 'AH' 

         ELSE 'AS' 

      END AS ASSIGNED_TYPE 

   FROM MRP_JO_DEMAND BB,

      PO_HD CC ,

      (

      SELECT JOB_ORDER_NO,

         DIMM_ID,

         SUM(RECV_QTY) AS RECV_QTY 

      FROM MRP_AGPO_SCHD_RECV_SPECIFIC 

      GROUP BY JOB_ORDER_NO,

         DIMM_ID 

      ) 

      REC,

      MRP_JO_ASSIGN DD 

   WHERE BB.JOB_ORDER_NO=CC.PO_NO 

  AND BB.JOB_ORDER_NO=REC.JOB_ORDER_NO(+) 

  AND BB.DIMM_ID=REC.DIMM_ID(+) 

  AND BB.JOB_ORDER_NO = DD.JOB_ORDER_NO(+) 

  AND BB.DIMM_ID = DD.DIMM_ID(+) 

  AND BB.MRP_GROUP_CD=DD.MRP_GROUP_CD(+) 

  AND EXISTS 

      (

      SELECT 1 

      FROM MRP_DIMM AA 

      WHERE AA.MRP_GROUP_CD=BB.MRP_GROUP_CD 

     AND AA.DIMM_ID=BB.DIMM_ID 

     AND AA.JOB_ORDER_NO=BB.JOB_ORDER_NO 

      ) 

   GROUP BY CC.SC_NO,

      BB.MRP_GROUP_CD, 

      BB.DIMM_ID, 

      BB.JOB_ORDER_NO,

      DD.REQ_DATE

   ) 

   A, 

   INVSUBMAT.INV_MRP_JO_AVAILABLE_V B 

WHERE A.JOB_ORDER_NO = B.JOB_ORDER_NO 

AND A.MRP_GROUP_CD = B.MRP_GROUP_CD 

AND A.DIMM_ID = B.DIMM_ID 

AND NVL (A.DEMAND_QTY, 0) < NVL (B.AVAILABLE_QTY, 0) 

AND NVL (B.AVAILABLE_QTY, 0)>0 

ORDER BY A.MRP_GROUP_CD,

   A.DIMM_ID,

   A.JOB_ORDER_NO;

查看执行计划,你会发现COST主要耗费在HASH JOIN上。如下截图所示,表INV_STOCK_ASSIGN来自于视图INVSUBMAT.INV_MRP_JO_AVAILABLE_V。

将上面复杂SQL拆分后,执行只需要不到一秒解决,如下截图所示,速率提高了几十倍。优化往往有时候很复杂,有时候也很简单,就是将复杂的语句拆分成简单的SQL语句,性能的提升有时候确实令人吃惊!

CREATE GLOBAL TEMPORARY TABLE TMP_MRP_MID_DATA

( SC_NO           VARCHAR2(20) ,

 MRP_GROUP_CD    VARCHAR2(10) ,

  DIMM_ID          NUMBER,

  JOB_ORDER_NO    VARCHAR2(20) ,

  DEMAND_QTY      NUMBER       ,

  DIFF_QTY        NUMBER       ,

  ASSIGNED_TYPE   VARCHAR(2)

) ON COMMIT PRESERVE ROWS;

 

 

INSERT INTO TMP_MRP_MID_DATA

SELECT A.SC_NO,

   A.MRP_GROUP_CD, 

   A.DIMM_ID, 

   A.JOB_ORDER_NO, 

   A.DEMAND_QTY,

   DECODE(SIGN(A.DEMAND_QTY),-1,0,A.DEMAND_QTY) AS DIFF_QTY, 

   A.ASSIGNED_TYPE 

FROM 

   (

   SELECT CC.SC_NO,

      BB.MRP_GROUP_CD, 

      BB.DIMM_ID, 

      BB.JOB_ORDER_NO, 

      NVL (SUM (BB.DEMAND_QTY), 0) - NVL(SUM(REC.RECV_QTY),0) AS DEMAND_QTY, 

      CASE 

         WHEN DD.REQ_DATE<TRUNC(SYSDATE) THEN 'AH' 

         ELSE 'AS' 

      END AS ASSIGNED_TYPE 

   FROM MRP_JO_DEMAND BB

      INNER JOIN  PO_HD CC ON BB.JOB_ORDER_NO=CC.PO_NO

        LEFT JOIN  (

      SELECT JOB_ORDER_NO,

         DIMM_ID,

         SUM(RECV_QTY) AS RECV_QTY 

      FROM MRP_AGPO_SCHD_RECV_SPECIFIC 

      GROUP BY JOB_ORDER_NO,

         DIMM_ID 

      ) 

   REC ON  BB.JOB_ORDER_NO=REC.JOB_ORDER_NO AND BB.DIMM_ID=REC.DIMM_ID

      LEFT JOIN  MRP_JO_ASSIGN DD ON BB.JOB_ORDER_NO = DD.JOB_ORDER_NO   AND BB.DIMM_ID = DD.DIMM_ID  AND BB.MRP_GROUP_CD=DD.MRP_GROUP_CD

      INNER JOIN MRP_DIMM AA  ON AA.MRP_GROUP_CD=BB.MRP_GROUP_CD   AND AA.DIMM_ID=BB.DIMM_ID    AND AA.JOB_ORDER_NO=BB.JOB_ORDER_NO  

   GROUP BY CC.SC_NO,

      BB.MRP_GROUP_CD, 

      BB.DIMM_ID, 

      BB.JOB_ORDER_NO,

      DD.REQ_DATE

   ) 

   A;

   COMMIT;

 

SELECT A.* FROM

TMP_MRP_MID_DATA A INNER JOIN

   INVSUBMAT.INV_MRP_JO_AVAILABLE_V B  ON A.JOB_ORDER_NO = B.JOB_ORDER_NO 

          AND A.MRP_GROUP_CD = B.MRP_GROUP_CD 

            AND A.DIMM_ID = B.DIMM_ID 

WHERE

NVL (A.DEMAND_QTY, 0) < NVL (B.AVAILABLE_QTY, 0) 

AND NVL (B.AVAILABLE_QTY, 0)>0 

ORDER BY A.MRP_GROUP_CD,

   A.DIMM_ID,

   A.JOB_ORDER_NO;

 

小结:

1:越是复杂的SQL语句,优化器越是容易选择一个糟糕的执行计划(优化器之所以难以选定最优的执行计划,是因为优化器要平衡选定最优执行路径的代价,不能一味为了选择最优执行计划,而将复杂SQL的所有执行路径都计算对比一遍,往往只能有选择性的选取一些执行路径计算对比,否则开销太大。而越是复杂的SQL,可选择的执行路径就是越多。

说得有点绕口,还是打个比方,比如你从广州到北京,如果就只有飞机(直飞),火车(直达)、汽车(直达)三种选择,那么想必你能很快给出一个最优的路线(例如,最快的是飞机、最省钱的是火车),但是如果飞机、火车、汽车都不能直达:假如火车票没有了直达,你必须中途转几次、飞机票也没有直达了,你需要转机,那么此时选择性复杂的情况,你就必须花费不少时间才能制定一个最优的计划了。 如果在复杂一点的情况,你从中国去美国,是不是有N种路径? 如果全部计算对比一遍各种可能的路径,估计你小脑袋不够用………………

2:执行计划是可以被重用的,越简单的SQL语句被重用的可能性越高。而复杂的SQL语句只要有一个字符发生变化就必须重新解析,然后再把这一大堆垃圾塞在内存里。可想而知,数据库的效率会何等低下。

3:如果SQL语句过分复杂,要么是业务有问题,要么是模型设计不当。可以说复杂的SQL反映出系统设计方面有不少问题和缺陷。

化繁为简——分解复杂的SQL语句的更多相关文章

  1. 数据库 基于索引的SQL语句优化之降龙十八掌(转)

    一篇挺不错的关于SQL语句优化的文章,因不知原始出处,故未作引用说明! 1 前言      客服业务受到SQL语句的影响非常大,在规模比较大的局点,往往因为一个小的SQL语句不够优化,导致数据库性能急 ...

  2. 关于索引的sql语句优化之降龙十八掌

    1 前言       客服业务受到SQL语句的影响非常大,在规模比较大的局点,往往因为一个小的SQL语句不够优化,导致数据库性能急剧下降,小型机idle所剩无几,应用服务器断连.超时,严重影响业务的正 ...

  3. sql语句优化SQL Server

    MS   SQL   Server查询优化方法查询速度慢的原因很多,常见如下几种 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)          2.I/O吞吐量小,形成了 ...

  4. Oracle性能优化之SQL语句

    1.SQL语句执行过程 1.1 SQL语句的执行步骤 1)语法分析,分析语句的语法是否符合规范,衡量语句中各表达式的意义. 2)语义分析,检查语句中涉及的所有数据库对象是否存在,且用户有相应的权限. ...

  5. ORACLE性能优化之SQL语句优化

    版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   操作环境:AIX +11g+PLSQL 包含以下内容: 1.  SQL语句执行过程 2.  优化器及执行计划 3.  合 ...

  6. oracle 基础SQL语句 多表查询 子查询 分页查询 合并查询 分组查询 group by having order by

    select语句学习 . 创建表 create table user(user varchar2(20), id int); . 查看执行某条命令花费的时间 set timing on: . 查看表的 ...

  7. 整理:sql语句优化之SQL Server

    . 增加服务器CPU个数;但是必须明白并行处理串行处理更需要资源例如内存.使用并行还是串行程是MsSQL自动评估选择的.单个任务分解成多个任务,就可 以在处理器上运行.例如耽搁查询的排序.连接.扫描和 ...

  8. 基于简单sql语句的sql解析原理及在大数据中的应用

    基于简单sql语句的sql解析原理及在大数据中的应用 李万鸿 老百姓呼吁打土豪分田地.共同富裕,总有一天会实现. 全面了解你所不知道的外星人和宇宙真想:http://pan.baidu.com/s/1 ...

  9. SqlServer和Oracle中一些常用的sql语句9 SQL优化

    --SQL查询优化 尽量避免使用or,not,distinct运算符,简化连接条件 /*Or运算符*/ use db_business go select * from 仓库 where 城市='北京 ...

随机推荐

  1. AOP的实现机制--转

    原文地址:http://www.iteye.com/topic/1116696 1 AOP各种的实现 AOP就是面向切面编程,我们可以从几个层面来实现AOP. 在编译器修改源代码,在运行期字节码加载前 ...

  2. spring源码分析之定时任务Scheduled注解

    1. @Scheduled 可以将一个方法标识为可定时执行的.但必须指明cron(),fixedDelay(),或者fixedRate()属性. 注解的方法必须是无输入参数并返回空类型void的. @ ...

  3. git diff ^M的消除

    这是由于换行符在不同的操作系统上定义的区别造成的. Windows用CR LF来定义换行,Linux用LF. CR全称是Carriage Return ,或者表示为\r, 意思是回车. LF全称是Li ...

  4. JQuery文件上传插件ajaxFileUpload在Asp.net MVC中的使用

    0 ajaxFileUpload简介 ajaxFileUpload插件是一个非常简单的基于Jquery的异步上传文件的插件,使用过程中发现很多与这个同名的,基于原始版本基础之上修改过的插件,文件版本比 ...

  5. 自己实现简单的AOP(二)引入Attribute 为方法指定增强对象

    话续前文 : 自己实现简单的AOP(一)简介 在前一篇文章中,对AOP的实现方式做了一个简单介绍.接下来,引入Attribute 为方法指定增强对象,由此实现一个简单的AOP. 注意:指定的是增强对象 ...

  6. [转]Teach Yourself Programming in Ten Years——用十年教会自己编程

    作者:Peter Norvig 译者:刘海粟 本文原文为:http://norvig.com/21-days.html 该翻译文档的PDF版可以在这里获得:http://download.csdn.n ...

  7. 【Win10开发】自定义标题栏

    UWP 现在已经可以自定义标题栏了,毕竟看灰色时间长了也会厌烦,开发者们还是希望能够将自己的UI做的更加漂亮,更加与众不同.那么废话不多说,我们开始吧! 首先要了解ApplicationViewTit ...

  8. 策略模式 - Strategy

    Strategy Pattern,定义算法家族,分别封装起来,互相之间可替换,此模式让算法的变化不会影响到使用算法的客户端. // 上下文类(Context):用一个ConcreteStratege来 ...

  9. 【C#进阶系列】22 CLR寄宿和AppDomain

    关于寄宿和AppDomain 微软开发CLR时,将它实现成包含在一个DLL中的COM服务器. 任何Windows应用程序都能寄宿(容纳)CLR.(简单来讲,就是CLR在一个DLL中,通过引用这个DLL ...

  10. 【nodejs笔记1】配置webstorm + node.js +express + mongodb开发博客的环境

    1. 安装webstorm 并破解 2. 安装node (以及express框架) 至官网下载并安装.(http://nodejs.org)v0.10.32   msi  安装后测试,打开命令行, c ...