11

Moim zamiarem jest uzyskanie paginowanego zestawu wyników klientów. Używam tego algorytmu, z Tom:Dlaczego Oracle ignoruje indeks z 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; 

Mam też indeks zdefiniowany w kolumnie „Klient” „imię”.

CREATE INDEX CUSTOMER_FIRST_NAME_TEST ON CUSTOMER (FIRST_NAME ASC); 

Kwerenda zwraca oczekiwaną resultset, ale z wyjaśnij plan Zwróć uwagę, że indeks nie jest używany:

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

Używam Oracle 11g. Ponieważ właśnie szukam pierwszych 20 wierszy, uporządkowanych według indeksowanej kolumny, spodziewałbym się użycia indeksu.

Dlaczego optymalizator Oracle ignoruje indeks? Zakładam, że jest to coś nie tak z algorytmem stronicowania, ale nie wiem, co.

Dzięki.

Odpowiedz

21

więcej niż prawdopodobne, że Twoja kolumna FIRST_NAME jest zerowa.

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> 

dodać NOT tam, aby go rozwiązać.

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 Byłoby miło wiedzieć, dlaczego firma Oracle nie może używać indeksu dla kolumny 'nullable'. – Andomar

+3

może. ale tylko jeśli co najmniej JEDNA kolumna w indeksie NIE JEST NULLABLe. widzisz, w oracle, klucze NULL nie znajdują się w indeksie. – DazzaL

+5

np .: 'SQL> utwórz indeks c na kliencie (first_name, id);' jeśli ID jest kolumną nie podlegającą zerowaniu, uzyskasz także pożądany wynik. jeśli nie masz żadnych kolumn, które nie są zerowalne (wierd), utwórz indeks funkcji, taki jak 'create index c na kliencie (first_name, 0);' literalna 0 zawsze ma wartość zerową, więc zmusza każdy wiersz zerowy do indeksu. – DazzaL

0

Pytasz o więcej kolumn niż first_name. Indeks na first_name zawiera tylko kolumnę first_name i odniesienie do tabeli. Aby odzyskać pozostałe kolumny, Oracle musi wykonać odnośnik do samej tabeli dla każdego wiersza. Większość baz danych próbuje tego uniknąć, jeśli nie mogą zagwarantować niskiej liczby rekordów.

Baza danych zwykle nie jest wystarczająco inteligentna, aby poznać efekty klauzuli where w kolumnie row_number. Jednak Twoja podpowiedź mogła zrobić lewy.

Być może tabela jest naprawdę mała, więc Oracle spodziewa się, że skanowanie tabeli będzie tańsze niż wyszukiwanie, nawet dla 20 wierszy.

Powiązane problemy