3

我对 Oracle 查询的执行计划有点困惑。这是在 IBM AIX 6.1 平台上的 Oracle Enterprise Edition 11.2.0.1.0 中。我有一个表 TEST1(100 万行)和另一个表 TEST2(50,000 行)。两个表都有相同的列。有一个视图创建为这两个表的联合。我在这个视图上发起一个查询,在 WHERE 子句中有一个索引列。我能发现的是索引没有被使用并且导致了全表扫描。稍微修改一下查询,它就开始使用索引。我想知道这种特殊的变化如何导致计划的改变。

请在下面找到完整的 DDL + DML。我已经给出了简化的例子。实际的架构和要求要复杂一些。事实上,所讨论的查询是由 OCI 代码生成器动态构建和执行的。我在这里的目的不是要获得替代方案,而是要真正了解计划更改背后的逻辑原因(在此之间,我是应用程序程序员而不是数据库管理员)。非常感谢您的帮助。

DROP TABLE TEST1 CASCADE CONSTRAINTS ;  
DROP TABLE TEST2 CASCADE CONSTRAINTS ;  

CREATE TABLE TEST1  
(  
    ID      NUMBER(20)     NOT NULL,  
    NAME    VARCHAR2(40),  
    DAY     NUMBER(20)  
)  
PARTITION BY RANGE (DAY)  
(  
    PARTITION P001 VALUES LESS THAN (2),  
    PARTITION P002 VALUES LESS THAN (3),  
    PARTITION P003 VALUES LESS THAN (4),  
    PARTITION P004 VALUES LESS THAN (5),  
    PARTITION P005 VALUES LESS THAN (6),  
    PARTITION P006 VALUES LESS THAN (7),  
    PARTITION P007 VALUES LESS THAN (8),  
    PARTITION P008 VALUES LESS THAN (9),  
    PARTITION P009 VALUES LESS THAN (10),  
    PARTITION P010 VALUES LESS THAN (11),  
    PARTITION P011 VALUES LESS THAN (12),  
    PARTITION P012 VALUES LESS THAN (13),  
    PARTITION P013 VALUES LESS THAN (14),  
    PARTITION P014 VALUES LESS THAN (15),  
    PARTITION P015 VALUES LESS THAN (16),  
    PARTITION P016 VALUES LESS THAN (17),  
    PARTITION P017 VALUES LESS THAN (18),  
    PARTITION P018 VALUES LESS THAN (19),  
    PARTITION P019 VALUES LESS THAN (20),  
    PARTITION P020 VALUES LESS THAN (21),  
    PARTITION P021 VALUES LESS THAN (22),  
    PARTITION P022 VALUES LESS THAN (23),  
    PARTITION P023 VALUES LESS THAN (24),  
    PARTITION P024 VALUES LESS THAN (25),  
    PARTITION P025 VALUES LESS THAN (26),  
    PARTITION P026 VALUES LESS THAN (27),  
    PARTITION P027 VALUES LESS THAN (28),  
    PARTITION P028 VALUES LESS THAN (29),  
    PARTITION P029 VALUES LESS THAN (30),  
    PARTITION P030 VALUES LESS THAN (31)  
) ;  

CREATE INDEX IX_ID on TEST1 (ID) INITRANS 4 STORAGE(FREELISTS 16) LOCAL  
(  
    PARTITION P001,  
    PARTITION P002,  
    PARTITION P003,  
    PARTITION P004,  
    PARTITION P005,  
    PARTITION P006,  
    PARTITION P007,  
    PARTITION P008,  
    PARTITION P009,  
    PARTITION P010,  
    PARTITION P011,  
    PARTITION P012,  
    PARTITION P013,  
    PARTITION P014,  
    PARTITION P015,  
    PARTITION P016,  
    PARTITION P017,  
    PARTITION P018,  
    PARTITION P019,  
    PARTITION P020,  
    PARTITION P021,  
    PARTITION P022,  
    PARTITION P023,  
    PARTITION P024,  
    PARTITION P025,  
    PARTITION P026,  
    PARTITION P027,  
    PARTITION P028,  
    PARTITION P029,  
    PARTITION P030  
) ;  

CREATE TABLE TEST2  
(  
    ID      NUMBER(20)      PRIMARY KEY   NOT NULL,  
    NAME    VARCHAR2(40),  
    DAY     NUMBER(20)  
) ;  

CREATE OR REPLACE VIEW TEST_V AS  
SELECT  
    ID, NAME, DAY  
FROM  
    TEST1  
UNION  
SELECT  
    ID, NAME, DAY  
FROM  
    TEST2 ;  

begin  
    for count in 1..1000000  
    loop  
        insert into test1 values(count, 'John', mod(count, 30) + 1) ;  
    end loop ;  
end ;  
/  

begin  
    for count in 1000000..1050000  
    loop  
        insert into test2 values(count, 'Mary', mod(count, 30) + 1) ;  
    end loop ;  
end ;  
/  

commit ;  

set lines 300 ;  
set pages 1000 ;  

-- Actual query  
explain plan for  
    SELECT Key FROM  
    (  
        WITH recs AS  
        (  
            SELECT * FROM TEST_V WHERE ID = 70000  
        )  
        (  
            SELECT 1 AS Key FROM recs WHERE NAME = 'John'  
        )  
        UNION  
        (  
            SELECT 2 AS Key FROM recs WHERE NAME = 'Mary'  
        )  
    ) ;  

select * from table(dbms_xplan.display()) ;  

PLAN_TABLE_OUTPUT  
------------------------------------------------------------------------------------------------------------------------------------------  
| Id  | Operation                   | Name                         | Rows  | Bytes  | TempSpc | Cost (%CPU)  |    Time  | Pstart | Pstop |  
------------------------------------------------------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT            |                              | 1611K | 4721K  |         |   13559 (1)  | 00:02:43 |        |       |  
|   1 |  VIEW                       |                              | 1611K | 4721K  |         |   13559 (1)  | 00:02:43 |        |       |  
|   2 |   TEMP TABLE TRANSFORMATION |                              |       |        |         |              |          |        |       |  
|   3 |    LOAD AS SELECT           | SYS_TEMP_0FD9D6610_34D3B6C   |       |        |         |              |          |        |       |  
|*  4 |     VIEW                    | TEST_V                       | 805K  | 36M    |         |   10403 (1)  | 00:02:05 |        |       |  
|   5 |      SORT UNIQUE            |                              | 805K  | 36M    |   46M   |   10403 (8)  | 00:02:05 |        |       |  
|   6 |       UNION-ALL             |                              |       |        |         |              |          |        |       |  
|   7 |        PARTITION RANGE ALL  |                              | 752K  | 34M    |         |   721 (1)    | 00:00:09 |    1   |   30  |  
|   8 |     TABLE ACCESS FULL       | TEST1                        | 752K  | 34M    |         |   721 (1)    | 00:00:09 |    1   |   30  |  
|   9 |        TABLE ACCESS FULL    | TEST2                        | 53262 | 2496K  |         |   68 (0)     | 00:00:01 |        |       |  
|  10 |    SORT UNIQUE              |                              | 1611K | 33M    |   43M   |   13559 (51) | 00:02:43 |        |       |  
|  11 |     UNION-ALL               |                              |       |        |         |              |          |        |       |  
|* 12 |      VIEW                   |                              | 805K  | 16M    |         |   1429 (1)   | 00:00:18 |        |       |  
|  13 |       TABLE ACCESS FULL     | SYS_TEMP_0FD9D6610_34D3B6C   | 805K  | 36M    |         |   1429 (1)   | 00:00:18 |        |       |  
|* 14 |      VIEW                   |                              | 805K  | 16M    |         |   1429 (1)   | 00:00:18 |        |       |  
|  15 |       TABLE ACCESS FULL     | SYS_TEMP_0FD9D6610_34D3B6C   | 805K  | 36M    |         |   1429 (1)   | 00:00:18 |        |       |  
------------------------------------------------------------------------------------------------------------------------------------------  

Predicate Information (identified by operation id):  
---------------------------------------------------  
   4 - filter("ID"=70000)  
  12 - filter("NAME"='John')  
  14 - filter("NAME"='Mary')  


-- Modified query (only change is absence of outermost SELECT)  
explain plan for  
    WITH recs AS  
    (  
        SELECT * FROM TEST_V WHERE ID = 70000  
    )  
    (  
        SELECT 1 AS Key FROM recs WHERE NAME = 'John'  
    )  
    UNION  
    (  
        SELECT 2 AS Key FROM recs WHERE NAME = 'Mary'  
    ) ;  

select * from table(dbms_xplan.display()) ;  

PLAN_TABLE_OUTPUT  
-----------------------------------------------------------------------------------------------------------------------------------------  
| Id  | Operation                                | Name                       | Rows  | Bytes | Cost (%CPU) | Time     | Pstart | Pstop |  
-----------------------------------------------------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT                         |                            |   4   |  88   |    6 (67)   | 00:00:01 |        |       |  
|   1 |  TEMP TABLE TRANSFORMATION               |                            |       |       |             |          |        |       |  
|   2 |   LOAD AS SELECT                         | SYS_TEMP_0FD9D6611_34D3B6C |       |       |             |          |        |       |  
|   3 |    VIEW                                  | TEST_V                     |   2   |  96   |    4 (50)   | 00:00:01 |        |       |  
|   4 |     SORT UNIQUE                          |                            |   2   |  96   |    4 (75)   | 00:00:01 |        |       |  
|   5 |      UNION-ALL                           |                            |       |       |             |          |        |       |  
|   6 |       PARTITION RANGE ALL                |                            |   1   |  48   |    1 (0)    | 00:00:01 |    1   |   30  |  
|   7 |        TABLE ACCESS BY LOCAL INDEX ROWID | TEST1                      |   1   |  48   |    1 (0)    | 00:00:01 |    1   |   30  |  
|*  8 |     INDEX RANGE SCAN                     | IX_ID                      |   1   |       |    1 (0)    | 00:00:01 |    1   |   30  |  
|   9 |       TABLE ACCESS BY INDEX ROWID        | TEST2                      |   1   |  48   |    1 (0)    | 00:00:01 |        |       |  
|* 10 |        INDEX UNIQUE SCAN                 | SYS_C001242692             |   1   |       |    1 (0)    | 00:00:01 |        |       |  
|  11 |   SORT UNIQUE                            |                            |   4   |  88   |    6 (67)   | 00:00:01 |        |       |  
|  12 |    UNION-ALL                             |                            |       |       |             |          |        |       |  
|* 13 |     VIEW                                 |                            |   2   |  44   |    2 (0)    | 00:00:01 |        |       |  
|  14 |      TABLE ACCESS FULL                   | SYS_TEMP_0FD9D6611_34D3B6C |   2   |  96   |    2 (0)    | 00:00:01 |        |       |  
|* 15 |     VIEW                                 |                            |   2   |  44   |    2 (0)    | 00:00:01 |        |       |  
|  16 |      TABLE ACCESS FULL                   | SYS_TEMP_0FD9D6611_34D3B6C |   2   |  96   |    2 (0)    | 00:00:01 |        |       |  
-----------------------------------------------------------------------------------------------------------------------------------------  

Predicate Information (identified by operation id):  
---------------------------------------------------  
   8 - access("ID"=70000)  
  10 - access("ID"=70000)  
  13 - filter("NAME"='John')  
  15 - filter("NAME"='Mary')  

quit ;  

谢谢和问候,
Reji

4

1 回答 1

2

我无法在 11.2.0.3 中重现这一点,除了:您遇到了一个错误,这显然在 11.2.0.3 中已解决,我认为对此行为没有合乎逻辑的解释。

One thing that jumped immediately in my eye is the lack of object statistics and - if your output was complete - the fact that OPTIMIZER_DYNAMIC_SAMPLING is set to 0. You could try to reproduce with OPTIMIZER_DYNAMIC_SAMPLING=2. In that case the dynamic sampler kicks in if the object statistics are missing. BTW: don't use this feature instead of correct optimizer statistics. More info about dynamic sampling Dynamic sampling and its impact on the Optimizer

In your - nice documented - question and script/test case you try to make use of append and nologging. This only works for bulk inserts, not for row inserts with values. What would happen is for every insert: push-up the highwater mark and dump a full block of data in the free block, in your case that would have only 1 row .... Luckily, the database ignores this instruction.

Before you fire SQL to a table, make sure that you give it optimizer statistics. This will certainly help your case.

于 2012-09-10T12:09:27.987 回答