2013-03-06 95 views
2

創建一個測試表:如何在使用複合索引時避免排序?

create table customer (first_name varchar2(20), last_name varchar2(20) not null, address varchar(20)); 

insert into customer select dbms_random.string('U', 20), dbms_random.string('U', 20), dbms_random.string('U', 20) from dual connect by level <= 100000; 
commit; 

create index i_ln_fn_0 on customer(last_name, first_name,0); — just to be sure that all rows are indexed 

現在解釋計劃:

explain plan for 
select /*+ FIRST_ROWS(20) */ * 
    from CUSTOMER 
    where first_name like 'AB%' 
    and first_name is not null 
    order by last_name; 

select * from table(dbms_xplan.display); 

------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |   | 197 | 12411 | 275 (2)| 00:00:04 | 
| 1 | SORT ORDER BY  |   | 197 | 12411 | 275 (2)| 00:00:04 | 
|* 2 | TABLE ACCESS FULL| CUSTOMER | 197 | 12411 | 274 (1)| 00:00:04 | 
------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter("FIRST_NAME" LIKE 'AB%' AND "FIRST_NAME" IS NOT NULL) 

但因爲我想只有第一排,我想避免的排序整個表的。我想有這樣一個計劃:

SELECT STATEMENT 
    TABLE ACCESS BY ROWID (customer) 
     INDEX FULL SCAN (i_ln_fn_0) 

如何說服數據庫,以避免排序?

問題更加嚴重。即使當我僅使用姓氏,無處不在:

explain plan for 
select /*+ FIRST_ROWS(20) */ last_name 
    from CUSTOMER 
    where last_name like 'AB%' 
    and last_name is not null 
    order by last_name; 

select * from table(dbms_xplan.display); 

------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |   | 17 | 357 |  4 (25)| 00:00:01 | 
| 1 | SORT ORDER BY |   | 17 | 357 |  4 (25)| 00:00:01 | 
|* 2 | INDEX RANGE SCAN| I_LN_FN_0 | 17 | 357 |  3 (0)| 00:00:01 | 
------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - access("LAST_NAME" LIKE 'AB%') 
     filter("LAST_NAME" LIKE 'AB%') 

這裏的排序真的沒有必要,但數據庫仍然使用它。爲什麼?

編輯:測試兩個

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production 
PL/SQL Release 11.2.0.1.0 - Production 
"CORE 11.2.0.1.0 Production" 
TNS for 64-bit Windows: Version 11.2.0.1.0 - Production 
NLSRTL Version 11.2.0.1.0 - Production 

Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production+ 
PL/SQL Release 11.2.0.2.0 - Production+ 
"CORE 11.2.0.2.0Production"+ 
TNS for IBM/AIX RISC System/6000: Version 11.2.0.2.0 - Production+ 
NLSRTL Version 11.2.0.2.0 - Production+ 

有同樣的計劃。

+0

我無法重現你的結果。實際上,我使用'INDEX FULL SCAN'來得到你正在尋找的計劃。你使用的是什麼版本的Oracle?你沒有忽略任何統計收集?在「謂詞信息」部分沒有獲得用於此語句的動態採樣(level = 2),這很奇怪。您是否將參數optimizer_dynamic_sampling更改爲0? – 2013-03-07 06:32:40

+0

奇怪,因爲我可以在我的個人(在我的筆記本上)和生產數據庫(我將db版本添加到原始問題)上重現問題。據我所知,數據庫不包含任何不尋常的調整。一切都很重要,默認值是V $ PARAMETER,optimizer_dynamic_sampling = 2。我用'exec dbms_stats.gather_table_stats('TEST','CUSTOMER',cascade => true)重新收集統計信息;'但沒有任何影響。 – xarx 2013-03-07 09:28:03

回答

2

原因是NLS_SORT參數的值錯誤。將其更改爲BINARY後,計劃開始變得像我想要的那樣。

http://docs.oracle.com/cd/E24693_01/server.11203/e24448/initparams152.htm

NLS_SORT的值會影響查詢的執行計劃。由於 標準索引不能用作排序在 語言順序中的值的源,因此通常必須執行顯式排序操作,而不是索引範圍掃描,而執行 。可定義NLSSORT 函數上的函數索引,以提供按語言順序排序的值 ,並將索引範圍掃描重新引入執行計劃。

(我已經得到了保羅Horth在forums.oracle COM這個答案。)