zoukankan      html  css  js  c++  java
  • SQL提高性能

    [转]SQL中使用WITH AS提高性能


    1. 案例起因

      公司门店应用程序每天都要出一份报表,用来统计所有商品当天的期初库存数量、入库数量、出库数量

      及当天的期末库存数量。运行半年以后,这份报表运行越来越慢,到现在,每次运行该报表显示当天数据时需要近20秒的时间。于是开发人员找到我,希望我看看,是不是可以使该报表运行的时间更短。

      该报表就是一段SQL语句,主要由三部分组成,第一部分是计算每个商品的期初数量,第二部分是计算每个商品的当天发生(包括入库和出库的)数量,第三部分是计算每个商品的期末数量,也就是当天的余额。每个部分使用UNION ALL连接起来。

      我看到该报表,第一个感觉就是这段SQL里的每个部分都要对表进行扫描,明显成本过高。应该可以使用WITH AS进行改写。

    2. WITH AS的含义

      WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到。有的时候,是为了让SQL语句的可读性更高些,也有可能是在UNION ALL的不同部分,作为提供数据的部分。

      特别对于UNION ALL比较有用。因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITH AS短语,则只要执行一遍即可。如果WITH AS短语所定义的表名被调用两次以上,则优化器会自动将WITH AS短语所获取的数据放入一个TEMP表里,如果只是被调用一次,则不会。而提示materialize则是强制将WITH AS短语里的数据放入一个全局临时表里。很多查询通过这种方法都可以提高速度。

    3. 案例说明

      首先介绍该SQL所涉及到的主要的表的结构。该表表名为fin,用来存放每天每个商品的发生数以及该商

      品的余额数。其表结构为如下所示(这里我只选取了与我们要讨论的SQL相关的部分表字段)。

      SQL> desc fin

      名称 是否为空? 类型

      ----------------------------------------- -------- ----------------------------

      。。。。。。

      DAY DATE

      SKU VARCHAR2(8)

      INQTY NUMBER(16,6)

      OUTQTY NUMBER(16,6)

      LASTQTY NUMBER(16,6)

      。。。。。。。。

      简单解释一下各个字段的含义:

      1) DAY:发生的日期。

      2) SKU:发生交易的商品代码。

      3) INQTY:商品入库数量。

      4) OUTQTY:商品出库数量。

      5) LASTQTY:商品的余额数量。

      该表中含有的记录数量为:

      SQL> SELECT count(*) FROM fin;

      COUNT(*)

      ----------

      4729319

    原来的SQL如下所示(比如查询2003年7月14日这天的记录。当然,我对该SQL做了些修改,去掉了与本文讨论无关的部分,比如显示商品名称之类的部分等):

      SELECT

      sku,

      sum(initqty) as initqty,

      sum(inqty) as inqty,sum(outqty) as outqty,

      sum(lastqty) as lastqty

      FROM (

      SELECT sku,lastqty as initqty,0 as inqty,0 as outqty,0 as lastqty

      FROM fin

      WHERE day=to_date('20030713','yyyymmdd')

      UNION ALL

      SELECT sku,0 as initqty,inqty,outqty,0 as lastqty

      FROM fin

      WHERE day>=to_date('20030714','yyyymmdd') and day<=to_date('20030714','yyyymmdd')

      UNION ALL

      SELECT sku,0 as initqty,0 as inqty,0 as outqty,lastqty

      FROM fin

      WHERE day=to_date('20030714','yyyymmdd')

      )

      GROUP BY sku;

      我们来看该SQL所花费的时间为:

      SQL> set timing on

      SQL> /

      。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

      SKU INITQTY INQTY OUTQTY LASTQTY

      -------- ---------- ---------- ---------- ----------

      00106162 0 0 12 60

      00106467 0 20 10 60

      已选择956行。

      已用时间: 00: 00: 19.08

      然后,我们来对该SQL进行改写一番,如下所示:

      WITH result AS (

      SELECT /*+ materialize */ day,sku,inqty,outqty,lastqty

      FROM fin

      WHERE day>=to_date('20030713','yyyymmdd') AND day<=to_date('20030714','yyyymmdd'))

      SELECT

      sku,

      sum(initqty) as initqty,

      sum(inqty) as inqty,

      sum(outqty) as outqty,

      sum(lastqty) as lastqty

      FROM (

      SELECT sku,lastqty as initqty,0 as inqty,0 as outqty,0 as lastqty

      FROM result

      WHERE day=to_date('20030713','yyyymmdd')

      UNION ALL

      SELECT sku,0 as initqty,inqty,outqty,0 as lastqty

      FROM result

      WHERE day=to_date('20030714','yyyymmdd')

      UNION ALL

      SELECT sku,0 as initqty,0 as inqty,0 as outqty,lastqty

      FROM result

      WHERE day=to_date('20030714','yyyymmdd')

      )

      GROUP BY sku;

      我们来看修改后的SQL所花费的时间为:

      SQL> set timing on

      SQL> /

      。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

      SKU INITQTY INQTY OUTQTY LASTQTY

      -------- ---------- ---------- ---------- ----------

      00106162 0 0 12 60

      00106467 0 20 10 60

      已选择956行。

      已用时间: 00: 00: 06.06

      从这里可以看到,通过WITH AS可以从20秒降低到6秒,几乎提高了65%的性能。

     

    [转]SQL Server 中Inner join 和where的效率差异

     

     

    今天,手头上正在作的一个项目,在生成报表时,客户感觉太慢,于是,各处检查,看可否提示效率。界面上的都改进了,提升不大。如是在SQL 语句上下功夫。(我这人比较懒,对简单的语句和查询都没有经过仔细优化的,一般只对姚使用left join,outer join,group by 以及carsor的语句会仔细写并用数据库理论考虑和检查---因为这种语句一般测试时如果发现错误,检查和调试很麻烦)

    先在网上Google搜索“Join 与 Where 效率”以及察看SQL Server 帮助文档,希望能获得“捷径”些的优化思路。

    搜索的结果是,各大论坛,包括MSDN上很多人提出了这个问题,但回答是众说纷纭。总体上总结出来时说:对小数据量(<N万)的来说效率几乎无差异,更有说法说Inner join 和Where只是SQL标准不同,在查询分析器中SQL Server查询分析器是将Where直接转换为Join后查询的。

    还是自己来做试验吧。

    如是有了如下比较结果(均在查询分析器中查询和计时):

    语句(1)
    declare @operatorName nvarchar(50)
    set @operatorName = '%'

     select distinct item.*  from item , customer_item , customer_operator ,operator
    where item.itemcode = customer_item.itemCode
    and customer_item.customerCode =  customer_operator.customerCode
    and customer_operator.operatorId =  customer_operator.operatorId
    and operator.operatorName like @operatorName
    and item.deleted = 0 and customer_item.deleted = 0 and customer_operator.deleted = 0
    查询结果,74行,共时间0:00:04

    语句(2)
    declare @operatorName nvarchar(50)

    set @operatorName = '%'

     select distinct item.*  from item inner join  customer_item
    on  item.itemcode = customer_item.itemCode
    inner join customer_operator on customer_item.customerCode = customer_operator.customerCode
    inner join operator on customer_operator.operatorId = operator.operatorId
    where  operator.operatorName like @operatorName
    and item.deleted = 0 and customer_item.deleted = 0 and customer_operator.deleted = 0
    共74行,时间0:00:01

    后检查发现语句(1)中有一个重复自查询条件 :customer_operator.operatorId =  customer_operator.operatorId
    将其叶加到语句2中,语句(3)
    declare @operatorName nvarchar(50)

    set @operatorName = '%'

     select distinct item.*  from item inner join  customer_item
    on  item.itemcode = customer_item.itemCode
    inner join customer_operator on customer_item.customerCode = customer_operator.customerCode
    inner join operator on customer_operator.operatorId = operator.operatorId
    where  operator.operatorName like @operatorName
    and item.deleted = 0 and customer_item.deleted = 0 and customer_operator.deleted = 0
    and customer_operator.operatorId =  customer_operator.operatorId

    所用时间和结果都为74行,时间0:00:01。

    将语句(1)中的去掉该条件后成为语句(4)
    declare @operatorName nvarchar(50)
    set @operatorName = '%'

     select distinct item.*  from item , customer_item , customer_operator ,operator
    where item.itemcode = customer_item.itemCode
    and customer_item.customerCode =  customer_operator.customerCode
    --and customer_operator.operatorId =  customer_operator.operatorId
    and operator.operatorName like @operatorName
    and item.deleted = 0 and customer_item.deleted = 0 and customer_operator.deleted = 0

    时间和结果为74行,时间0:00:01。

    终于发现了些他们的差异。

    结论:
             尽量使用Join 而不是Where来列出关联条件,特别是多个表联合的时候。
    原因是:
                (1)在效率上,Where可能具有和Inner join一样的效率。但基本可以肯定的(通过SQLServer帮助和其它资料,以及本测试)是Join的效率不比Where差。
                (2)使用Join可以帮助检查语句中的无效或者误写的关联条件

     

     

     

    以及在pl/sql中使用profile来查看语句执行的消耗

     

     

    PL/SQL Developer - The PL/SQL Profiler

    SCREENSHOT

    The PL/SQL Profiler is a very powerful tool to help you optimize your PL/SQL code, which is available on Oracle8i and later. For each executed line of code, the total time, maximum time, minimum time, and the number of occurrences will be determined.
    The Profiler is easily accessible in the Debugger's Test Window. Before executing a Test Script, simply press the Create Profiler report button on the toolbar. If you subsequently execute the script, you can switch to the Profiler page to view the report.

     

    PL/SQL Developer - The Integrated Debugger

    Screenshot

    The Integrated Debugger is a very powerful tool for those problems that are hard to fix. And yet it is very easy to use. No extra actions or conversions are needed and no limitations exist. Just execute the program unit in the Debugger and step through your code. You can step into any program unit, including triggers and types. Breakpoints can be set or removed by simply clicking in the left margin of the Debugger or Program Editor. You can additionally define conditions and messages for breakpoints.

    Variable values can be examined in the Debugger by moving the mouse cursor over the variable name in the source. In the screenshot above, the mouse cursor is moved over the 'p_DeptNo' variable and its value is automatically displayed in a popup. You can set the value of a variable in a similar way.

    When an exception occurs, the Debugger will take you to the line that causes it and you are able to view variable values at the time of the exception. This can obviously be very helpful to find the cause of the exception.

    On Oracle8i or later, you can use the Test Window's Trace facility to trace the execution of your PL/SQL code. You can configure which events you want to trace: Calls, Exceptions, SQL, or even every executed line of PL/SQL code.

  • 相关阅读:
    Python 函数知识点
    面向对象相关
    判断arg参数是否是可以被调用的
    利用U盘安装CentOS7系统
    简单模仿OpenGL中的栈的作用
    温故而知新我再一次学习库
    关于帧缓存的总结
    OGRE的相关工具和库
    OpenGL在Qt界面下的应用(helloworld)
    OpenGL加载Cg程序
  • 原文地址:https://www.cnblogs.com/SEEKTHINKING/p/2023503.html
Copyright © 2011-2022 走看看