11

La mia intenzione è ottenere un set di risultati impaginato dei clienti. Sto usando questo algoritmo, da Tom:Perché Oracle ignora l'indice con 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; 

Ho anche un indice definito sulla colonna "CLIENTE" "FIRST_NAME":.

CREATE INDEX CUSTOMER_FIRST_NAME_TEST ON CUSTOMER (FIRST_NAME ASC); 

La query restituisce il gruppo di risultati attesi, ma dal spiegare il piano noto che l'indice non è utilizzato:

-------------------------------------------------------------------------------------- 
| 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) 

Sto usando Oracle 11g. Poiché ho appena richiesto le prime 20 righe, ordinate dalla colonna indicizzata, mi aspetto che venga utilizzato l'indice.

Perché l'ottimizzatore Oracle ignora l'indice? Immagino sia qualcosa di sbagliato nell'algoritmo di impaginazione, ma non riesco a capire cosa.

Grazie.

risposta

21

più che probabile la colonna FIRST_NAME è annullabile.

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> 

aggiungere un NON NULL in là per risolverlo.

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 Sarebbe bello sapere perché Oracle non può utilizzare un indice per una colonna 'nullable'. – Andomar

+3

può. ma solo se almeno UNA colonna nell'indice NON è NULLABLE. vedi, in oracolo, le chiavi NULL non sono nell'indice. – DazzaL

+5

ad esempio: 'SQL> crea l'indice c sul cliente (first_name, id);' se l'ID è una colonna non annullabile si ottiene anche il risultato desiderato. se non hai colonne che non sono annullabili (wierd), allora crea un indice di funzione come 'crea l'indice c sul cliente (first_name, 0);' lo zero letterale non è sempre nullo, quindi forza ogni riga nulla nell'indice. – DazzaL

0

Stai richiedendo più colonne di first_name. L'indice su first_name contiene solo la colonna first_name e un riferimento alla tabella. Quindi, per recuperare le altre colonne, Oracle deve eseguire una ricerca sul tavolo stesso per ogni riga. La maggior parte dei database cerca di evitare questo se non possono garantire un conteggio record basso.

Un database non è in genere abbastanza intelligente per conoscere gli effetti di una clausola where su una colonna row_number. Tuttavia, il tuo suggerimento /*+ FIRST_ROWS(20) */ potrebbe aver fatto il trucco.

Forse la tabella è davvero piccola, così che Oracle si aspetta che la scansione della tabella sia più economica delle ricerche, anche solo per 20 righe.