2012-09-10 249 views
3

我對Oracle查詢的執行計劃有點困惑。這在平臺IBM AIX 6.1上的Oracle企業版11.2.0.1.0中。我有一個表TEST1(100萬行)和另一個表TEST2(50,000行)。兩個表都有相同的列。有一個視圖創建爲這兩個表的聯合。我在這個視圖上觸發查詢,在WHERE子句中有一個索引列。我能找到的是該索引未被使用,並且導致了全表掃描。稍微修改查詢後,它開始使用索引。我想知道這個特定的改變會如何導致計劃的改變。Oracle查詢執行計劃

請在下面找到完整的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

+2

我已經在[sqlfiddle](http://sqlfiddle.com/#!4/c91d7/1)上覆制了這個問題: –

+0

你給解釋計劃提供了所有輸出嗎? –

+1

感謝弗洛林。我第一次看到這個網站(sqlfiddle)。如果我知道,我可能會在那裏創建樣本。在發佈之前花了差不多半個小時的時間格式化計劃:(圖形計劃看起來非常漂亮和有用 – mpathi

回答

2

我不能在11.2.0.3重現此,我不認爲有這種行爲除了一個合乎邏輯的解釋:你打一個bug,那顯然是在11.2.0.3中解決的。

在我眼中立即跳起來的一件事是缺少對象統計信息和 - 如果您的輸出已完成 - OPTIMIZER_DYNAMIC_SAMPLING設置爲0的事實。您可以嘗試使用OPTIMIZER_DYNAMIC_SAMPLING = 2重現。在這種情況下,如果對象統計信息丟失,則動態採樣器會啓動。順便說一句:不要使用此功能,而不是正確的優化器統計信息。更多關於動態採樣的信息Dynamic sampling and its impact on the Optimizer

在你的 - 很好的記錄 - 問題和腳本/測試案例中,你嘗試使用append和nologging。這僅適用於批量插入,不適用於具有值的行插入。會發生什麼對於每個插入:上推高位標記並在空閒塊中轉儲一整塊數據,在您的情況下只有1行....幸運的是,數據庫忽略該指令。

在將SQL引發到表之前,請確保您爲其提供優化程序統計信息。這肯定會有助於你的情況。

+0

謝謝ik_zelf。我給出的計劃已經完成,並且計劃下面的「注」表示動態採樣(級別2),我也會嘗試一下11.2.0.3.0關於/ * + append * /提示和nologging,謝謝糾正我,我在我的盒子裏嘗試了查詢和數據的各種組合。我只是想避免在這些實驗中創建的日誌。快速的谷歌搜索提示了這個提示,我沒有打擾驗證,因爲它只是秒殺代碼。 – mpathi