代码之家  ›  专栏  ›  技术社区  ›  Andy Lester

为什么Oracle的查询计划器要添加一个复制约束的筛选器谓词?

  •  3
  • Andy Lester  · 技术社区  · 7 年前

    SELECT
        u.custid AS "custid",
        l.listid AS "listid"
    FROM
        users u
        INNER JOIN lists l ON u.custid = l.custid
    

    下面是autotrace解释告诉我的计划

    ------------------------------------------------------------------------------------------
    | Id  | Operation             | Name     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT      |          |  1468K|    29M|       | 11548   (1)| 00:00:01 |
    |*  1 |  HASH JOIN            |          |  1468K|    29M|  7104K| 11548   (1)| 00:00:01 |
    |   2 |   INDEX FAST FULL SCAN| USERS_PK |   404K|  2367K|       |   266   (2)| 00:00:01 |
    |*  3 |   TABLE ACCESS FULL   | LISTS    |  1416K|    20M|       |  9110   (1)| 00:00:01 |
    ------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - access("U"."CUSTID"="L"."CUSTID")
       3 - filter(TRUNC("SORT_TYPE")>=1 AND TRUNC("SORT_TYPE")<=16)
    
    Note
    -----
       - dynamic statistics used: dynamic sampling (level=2)
       - this is an adaptive plan
       - 1 Sql Plan Directive used for this statement
    

    我关心的是谓词3。 sort_type 排序类型

    那里 限制 lists.sort_type )是的,我知道我们可以 排序类型 是整数而不是数字)

    sort_type   NUMBER DEFAULT 2 NOT NULL,
        CONSTRAINT lists_sort_type CHECK ( sort_type BETWEEN 1 AND 16 AND TRUNC(sort_type) = sort_type )
    

    排序类型 基本上是重言式的。每行 lists 由于该约束,必须通过该筛选器。

    我之所以担心,是因为我在一个更大、更复杂的查询中发现了这个过滤器,我试图在几分钟的运行时间内对其进行优化。

    为什么Oracle要添加该过滤器,这是一个问题和/或指向另一个问题?

    排序类型 没有 TRUNC

    1 回复  |  直到 7 年前
        1
  •  3
  •   Matthew McPeak    7 年前

    一般来说,Oracle根据您的 CHECK 无论何时这样做,约束都将为优化器提供更多信息,以生成一个好的计划。当这些是多余的时,识别它们并不总是足够聪明的。以下是Oracle 12c中使用表名的一个简短示例:

    -- Create the CUSTS table
    CREATE TABLE custs ( custid number not null );
    CREATE INDEX custs_u1 on custs (custid);
    
    -- Create the LISTS table
    CREATE TABLE lists 
      ( listid number not null, 
        sort_type number not null, 
        custid number,
        constraint lists_c1 check ( sort_type between 1 and 16 and
              trunc(sort_type) = sort_Type )
    );
    
    -- Explain a join
    EXPLAIN PLAN FOR
    SELECT /*+ USE_HASH(u) */ 
           u.custid AS "custid",
           l.listid AS "listid"
    FROM custs u
    INNER JOIN lists l ON u.custid = l.custid;
    
    -- Show the plan
    select * from table(dbms_xplan.display);
    
    Plan hash value: 2443150416
    
    -------------------------------------------------------------------------------
    | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
    -------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT   |          |     1 |    39 |     3   (0)| 00:00:01 |
    |*  1 |  HASH JOIN         |          |     1 |    39 |     3   (0)| 00:00:01 |
    |   2 |   INDEX FULL SCAN  | CUSTS_U1 |     1 |    13 |     1   (0)| 00:00:01 |
    |   3 |   TABLE ACCESS FULL| LISTS    |     1 |    26 |     2   (0)| 00:00:01 |
    -------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - access("U"."CUSTID"="L"."CUSTID")
    
    Note
    -----
       - dynamic statistics used: dynamic sampling (level=2)
    

    到目前为止,没有什么奇怪的。没有添加可疑谓词。

    现在,让我们告诉Oracle优化器 TRUNC(sort_type)

    declare
      x varchar2(30);
    begin
      x := dbms_stats.create_extended_stats ( user, 'LISTS', '(TRUNC(SORT_TYPE))');
      dbms_output.put_line('Extension name = ' || x);
    end;
    

    ... 现在,让我们再次解释同样的问题。。。

    -- Re-explain the same join as before
    EXPLAIN PLAN FOR
    SELECT /*+ USE_HASH(u) */ 
           u.custid AS "custid",
           l.listid AS "listid"
    FROM custs u
    INNER JOIN lists l ON u.custid = l.custid;
    
    -- Show the new plan
    select * from table(dbms_xplan.display);
    
    Plan hash value: 2443150416
    
    -------------------------------------------------------------------------------
    | Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
    -------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT   |          |     1 |    52 |     3   (0)| 00:00:01 |
    |*  1 |  HASH JOIN         |          |     1 |    52 |     3   (0)| 00:00:01 |
    |   2 |   INDEX FULL SCAN  | CUSTS_U1 |     1 |    13 |     1   (0)| 00:00:01 |
    |*  3 |   TABLE ACCESS FULL| LISTS    |     1 |    39 |     2   (0)| 00:00:01 |
    -------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - access("U"."CUSTID"="L"."CUSTID")
       3 - filter(TRUNC("SORT_TYPE")>=1 AND TRUNC("SORT_TYPE")<=16)
    
    Note
    -----
       - dynamic statistics used: dynamic sampling (level=2)
    

    (*)在以前的版本中有很多bug,这些bug破坏了CBO估计的选择性,从而损害了事情。

    扩展统计数据的存在只是Oracle认为它可以从这个谓词中获益的一个例子。要确定这是否是您的情况中的原因,您可以在数据库中查找扩展统计信息,如下所示:

    SELECT * FROM dba_stat_extensions where table_name = 'LISTS';
    

    请记住,Oracle CBO可以自行创建stat扩展。所以可能会有一些你没有意识到的扩展数据。

    推荐文章