11

私の意図は、顧客のページ区切りの結果を得ることです。私はまた、インデックスが列「顧客」に定義されているOracleがORDER BYでインデックスを無視するのはなぜですか?

select * from (
    select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN 
    from CUSTOMER C 
) 
where RN between 1 and 20 
order by RN; 

「FIRST_NAME」を::

CREATE INDEX CUSTOMER_FIRST_NAME_TEST ON CUSTOMER (FIRST_NAME ASC); 

クエリが期待される結果セットを返しますが、から。私はTomから、このアルゴリズムを使用しています私はインデックスが使用されていないことに気づきます:

-------------------------------------------------------------------------------------- 
| Id | Operation     | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
-------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |   | 15467 | 679K| 157 (3)| 00:00:02 | 
| 1 | SORT ORDER BY   |   | 15467 | 679K| 157 (3)| 00:00:02 | 
|* 2 | VIEW     |   | 15467 | 679K| 155 (2)| 00:00:02 | 
|* 3 | WINDOW SORT PUSHED RANK|   | 15467 | 151K| 155 (2)| 00:00:02 | 
| 4 |  TABLE ACCESS FULL  | CUSTOMER | 15467 | 151K| 154 (1)| 00:00:02 | 
-------------------------------------------------------------------------------------- 

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

    2 - filter("RN">=1 AND "RN"<=20) 
    3 - filter(ROW_NUMBER() OVER (ORDER BY "FIRST_NAME")<=20) 

私はOracle 11gを使用しています。私はインデックスされた列で順序付けられた最初の20行を照会するだけなので、インデックスを使用することが期待されます。

Oracleオプティマイザがインデックスを無視するのはなぜですか?私はそれがページネーションアルゴリズムに何か間違っていると思いますが、何が分かりません。

ありがとうございました。

答えて

21

あなたのFIRST_NAME列はNULL可能です。

SQL> create table customer (first_name varchar2(20), last_name varchar2(20)); 

Table created. 

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

100000 rows created. 

SQL> create index c on customer(first_name); 

Index created. 

SQL> explain plan for select * from (
    2 select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN 
    3 from CUSTOMER C 
    4 ) 
    5 where RN between 1 and 20 
    6 order by RN; 

Explained. 

SQL> @explain "" 

Plan hash value: 1474094583 

---------------------------------------------------------------------------------------------- 
| Id | Operation     | Name  | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |   | 117K| 2856K|  | 1592 (1)| 00:00:20 | 
| 1 | SORT ORDER BY   |   | 117K| 2856K| 4152K| 1592 (1)| 00:00:20 | 
|* 2 | VIEW     |   | 117K| 2856K|  | 744 (2)| 00:00:09 | 
|* 3 | WINDOW SORT PUSHED RANK|   | 117K| 1371K| 2304K| 744 (2)| 00:00:09 | 
| 4 |  TABLE ACCESS FULL  | CUSTOMER | 117K| 1371K|  | 205 (1)| 00:00:03 | 
---------------------------------------------------------------------------------------------- 

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

    2 - filter("RN">=1 AND "RN"<=20) 
    3 - filter(ROW_NUMBER() OVER (ORDER BY "FIRST_NAME")<=20) 

Note 
----- 
    - dynamic sampling used for this statement (level=2) 

21 rows selected. 

SQL> alter table customer modify first_name not null; 

Table altered. 

SQL> explain plan for select * from (
    2 select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN 
    3 from CUSTOMER C 
    4 ) 
    5 where RN between 1 and 20 
    6 order by RN; 

Explained. 

SQL> @explain "" 

Plan hash value: 1725028138 

---------------------------------------------------------------------------------------- 
| Id | Operation    | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT  |  | 117K| 2856K|  | 850 (1)| 00:00:11 | 
| 1 | SORT ORDER BY   |  | 117K| 2856K| 4152K| 850 (1)| 00:00:11 | 
|* 2 | VIEW     |  | 117K| 2856K|  |  2 (0)| 00:00:01 | 
|* 3 | WINDOW NOSORT STOPKEY|  | 117K| 1371K|  |  2 (0)| 00:00:01 | 
| 4 |  INDEX FULL SCAN  | C | 117K| 1371K|  |  2 (0)| 00:00:01 | 
---------------------------------------------------------------------------------------- 

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

    2 - filter("RN">=1 AND "RN"<=20) 
    3 - filter(ROW_NUMBER() OVER (ORDER BY "FIRST_NAME")<=20) 

Note 
----- 
    - dynamic sampling used for this statement (level=2) 

21 rows selected. 

SQL> 

これを解決するにはNOT NULLを追加します。

SQL> explain plan for select * from (
    2 select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN 
    3 from CUSTOMER C 
    4 where first_name is not null 
    5 ) 
    6 where RN between 1 and 20 
    7 order by RN; 

Explained. 

SQL> @explain "" 

Plan hash value: 1725028138 

---------------------------------------------------------------------------------------- 
| Id | Operation    | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT  |  | 117K| 2856K|  | 850 (1)| 00:00:11 | 
| 1 | SORT ORDER BY   |  | 117K| 2856K| 4152K| 850 (1)| 00:00:11 | 
|* 2 | VIEW     |  | 117K| 2856K|  |  2 (0)| 00:00:01 | 
|* 3 | WINDOW NOSORT STOPKEY|  | 117K| 1371K|  |  2 (0)| 00:00:01 | 
|* 4 |  INDEX FULL SCAN  | C | 117K| 1371K|  |  2 (0)| 00:00:01 | 
---------------------------------------------------------------------------------------- 

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

    2 - filter("RN">=1 AND "RN"<=20) 
    3 - filter(ROW_NUMBER() OVER (ORDER BY "FIRST_NAME")<=20) 
    4 - filter("FIRST_NAME" IS NOT NULL) 

Note 
----- 
    - dynamic sampling used for this statement (level=2) 

22 rows selected. 

SQL> 
+1

+1オラクルが「NULL可能」列に索引を使用できない理由を知ってうれしいです。 – Andomar

+3

できます。ただし、索引内の少なくとも1つの列がNOT NULLABLeである場合のみです。あなたは、oracleでNULLキーがインデックスにないことがわかります。 – DazzaL

+5

例: 'SQL>顧客のインデックスcを作成する(first_name、id);' IDがヌル値ではない列であれば、望みの結果も得られます。リテラル0は常にヌルではないので、各ヌル行を強制的にインデックスに入れるような関数インデックスを作成します(first_name、0)。 – DazzaL

0

first_nameより多くの列をクエリしています。 first_nameのインデックスには、first_nameの列とそのテーブルへの参照が含まれています。したがって、他の列を取得するには、各行に対して表自体を検索する必要があります。ほとんどのデータベースは、低いレコード数を保証できない場合、これを回避しようとします。

row_number列のwhere句の効果を知るには、データベースは通常はスマートではありません。しかし、あなたのヒント/*+ FIRST_ROWS(20) */がこのトリックを行った可能性があります。

おそらく、表が実際には小さいので、Oracleでは表のスキャンがルックアップよりも安価であると予想しています(わずか20行ですら)。

関連する問題