zoukankan      html  css  js  c++  java
  • 相关列问题

     先来创建一个表T

    create table t as select level as id ,level||'a' as a,level||level||'b' as b from dual connect by level<100;

    这里A列的值能够确定B列的值,

    insert into t  select * from t; 
    .............................. 一直重复插入数据

    SQL> select count(*) from t;

      COUNT(*)
    ----------
       3244032

    create index idx1 on t(a);  
    create index idx2 on t(a,b);   
      
    BEGIN
      DBMS_STATS.GATHER_TABLE_STATS(ownname          => 'SCOTT',
                                    tabname          => 'T',
                                    estimate_percent => 100,
                                    method_opt       => 'for all columns size skewonly',
                                    no_invalidate    => FALSE,
                                    degree           => 8,
                                    cascade          => TRUE);
    END;
    /

    SQL> select * from t where a='1a' and b='11b';
    
    已选择32768行。
    
    已用时间:  00: 00: 03.98
    
    执行计划
    ----------------------------------------------------------
    Plan hash value: 2303463401
    
    ------------------------------------------------------------------------------------
    | Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |      |   331 |  3972 |    84   (0)| 00:00:02 |
    |   1 |  TABLE ACCESS BY INDEX ROWID| T    |   331 |  3972 |    84   (0)| 00:00:02 |
    |*  2 |   INDEX RANGE SCAN          | IDX2 |   331 |       |     3   (0)| 00:00:01 |
    ------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       2 - access("A"='1a' AND "B"='11b')
    
    
    统计信息
    ----------------------------------------------------------
              1  recursive calls
              0  db block gets
          11838  consistent gets
           7943  physical reads
              0  redo size
         441749  bytes sent via SQL*Net to client
          24424  bytes received via SQL*Net from client
           2186  SQL*Net roundtrips to/from client
              0  sorts (memory)
              0  sorts (disk)
          32768  rows processed
    
    

    因为CBO不知道A与B关系,所以计算基数等于331,

    SQL> select 1/99/99*3244032 from dual; ----这个其实就是 a选择性*b选择性 =(1/99)*(1/99)

    1/99/99*3244032
    ---------------
         330.989899

    但是实际上它要返回32768条记录

    SQL> select * from t where a='1a';
    
    已选择32768行。
    
    已用时间:  00: 00: 01.38
    
    执行计划
    ----------------------------------------------------------
    Plan hash value: 1601196873
    
    --------------------------------------------------------------------------
    | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------
    |   0 | SELECT STATEMENT  |      | 32768 |   384K|  1874   (8)| 00:00:23 |
    |*  1 |  TABLE ACCESS FULL| T    | 32768 |   384K|  1874   (8)| 00:00:23 |
    --------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter("A"='1a')
    
    
    统计信息
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
          10120  consistent gets
           6312  physical reads
              0  redo size
         441749  bytes sent via SQL*Net to client
          24424  bytes received via SQL*Net from client
           2186  SQL*Net roundtrips to/from client
              0  sorts (memory)
              0  sorts (disk)
          32768  rows processed
    
    

    如果where条件单独是 where a='1a' CBO 就能够算对基数,它的基数是这样计算的

    SQL> select 3244032/99 from dual;

    3244032/99
    ----------
         32768 

    很显然,这个SQL select * from t where a='1a' and b='11b' 的执行计划走错了,它应该走全表扫描,但是因为计算基数错误,导致它走 IDX2这个索引

    相关列的解决办法在Oracle中有2个,一个是动态采样,另外一个就是Oracle11g,对相关列收集扩展统计

    SQL> ALTER SESSION SET optimizer_dynamic_sampling=6;
    
    会话已更改。
    
    SQL> set lines 200
    SQL> set pages 200
    SQL> set timi on
    SQL> explain plan for select * from t where a='1a' and b='11b';
    
    已解释。
    
    已用时间:  00: 00: 00.86
    SQL> select * from table(dbms_xplan.display);
    
    PLAN_TABLE_OUTPUT
    ---------------------------------------------------------------------------------------
    Plan hash value: 1601196873
    
    --------------------------------------------------------------------------
    | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------
    |   0 | SELECT STATEMENT  |      | 32776 |   384K|  1885   (8)| 00:00:23 |
    |*  1 |  TABLE ACCESS FULL| T    | 32776 |   384K|  1885   (8)| 00:00:23 |
    --------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter("A"='1a' AND "B"='11b')
    
    Note
    -----
       - dynamic sampling used for this statement
    
    已选择17行。

    设置动态采样之后 Oracle评估基数就基本正确了,关于11g扩展统计这里就不做了,有兴趣的请自己做一下。

    我对相关列的建议就是,能否在程序里拼接?如果A能确定B,那么做DB 设计的时候就不要创建B列了 直接在程序里根据A列的值生成B的值 这样减少DB的存储空间。

    如果非要在DB里设置B列,写SQL的时候就不要把2个列都写进去,也就是说不要写成
    select * from t where a='1a' and b='11b';

    直接写成
    select * from t where a='1a'  或者 select * from t where b='11b'

    这样能尽量避免CBO计算基数出错,如果这个表要与多表关联,基数一旦算错,必然导致整个SQL的执行计划全部出错,从而导致SQL性能下降。

    动态采样和扩展统计虽然是解决办法,但是如果产品要考虑兼容性呢?我的产品要同时支持ORACLE,DB2,SQLSERVER,甚至以后的国产数据库达梦,他们没有动态采样怎么办。

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    现在使用控件, 更喜欢继承(覆盖控件已有的函数,很奇怪的一种使用方式)
    Controls 属性与继承 TShape 类的小练习(使用TShape可以解决很多图形问题)
    QT创建窗口程序、消息循环和WinMain函数(为主线程建立了一个QEventLoop,并执行exec函数)
  • 原文地址:https://www.cnblogs.com/hehe520/p/6330552.html
Copyright © 2011-2022 走看看