2012-02-03 15 views
7

Aby wykonać zapytanie top-n wierszy w Oracle, ogólne jest użycie ROWNUM. Więc następujące zapytanie wydaje się ok (dostaje ostatniego 5-płatności):Oracle ROWNUM wydajność

select a.paydate, a.amount 
from (
    select t.paydate, t.amount 
    from payments t 
    where t.some_id = id 
    order by t.paydate desc 
) a 
where rownum <= 5; 

Ale dla bardzo dużych tabel, jest nieefektywne - dla mnie to prowadzony przez ~ 10 minut. Więc próbowałem innych zapytań, a skończyło się tym jednym, która biegnie przez mniej niż sekundę:

select * 
from (
    select a.*, rownum 
    from (select t.paydate, t.amount 
     from payments t 
     where t.some_id = id 
     order by t.paydate desc) a 
) 
where rownum <= 5; 

, aby dowiedzieć się, co się dzieje, patrzyłem planów wykonania dla każdego zapytania. Dla pierwszej kwerendy:

SELECT STATEMENT, GOAL = ALL_ROWS 7 5 175 
COUNT STOPKEY   
VIEW 7 5 175 
TABLE ACCESS BY INDEX ROWID 7 316576866 6331537320 
INDEX FULL SCAN DESCENDING 4 6 

a dla drugiego

SELECT STATEMENT, GOAL = ALL_ROWS 86 5 175 
COUNT STOPKEY   
VIEW 86 81 2835 
COUNT   
VIEW 86 81 1782 
SORT ORDER BY 86 81 1620 
TABLE ACCESS BY INDEX ROWID 85 81 1620 
INDEX RANGE SCAN 4 81 

Oczywiście jest INDEX pełne skanowanie MALEJĄCEJ sprawia, że ​​pierwsze zapytanie nieefektywne dla dużych tabelach. Ale nie mogę naprawdę odróżnić logiki dwóch pytań, patrząc na nie. Czy ktoś mógłby mi wyjaśnić logiczne różnice między dwoma pytaniami w ludzkim języku?

Z góry dziękuję!

+2

id jest zmienną powiązania, nie (powinno być: id?) Jeśli tak, to jaka wartość jest używana (tak samo?) – tbone

+2

Nie sądzę, że 'rownum', którego używasz do filtra w drugiej wersji jest gwarantowane jest to samo, co w pierwszym; myślę, że musisz albo alias swoje drugie zapytanie i odniesienia, lub dodać 'order by rownum' w zapytaniu przeciwko' a'? Wątpię, żeby to miało wpływ na prędkość. –

Odpowiedz

3

Po pierwsze, jak wspomniano w komentarzu Alexa, nie jestem pewien, czy twoja druga wersja jest w 100% gwarantowana, aby dać ci właściwe wiersze - ponieważ "środkowy" blok zapytania nie ma wyraźnego order by , Oracle nie ma obowiązku przekazywania wierszy do zewnętrznego bloku zapytania w dowolnej kolejności. Jednak nie wydaje się, aby istniał jakiś szczególny powód, dla którego zmieniłaby kolejność przekazywania rzędów z najbardziej wewnętrznego bloku, więc w praktyce prawdopodobnie będzie działać.

I dlatego firma Oracle wybiera inny plan dla drugiego zapytania - logicznie nie jest w stanie zastosować operacji STOPKEY do najbardziej wewnętrznego bloku zapytań.

Myślę, że w pierwszym przypadku optymalizator zakłada, że ​​wartości id są dobrze rozłożone i, dla dowolnej wartości, prawdopodobnie wystąpią bardzo niedawne transakcje. Ponieważ może zobaczyć, że potrzebuje tylko znaleźć 5 ostatnich dopasowań, oblicza, że ​​wydajniejsze jest skanowanie wierszy w porządku malejącym według paydate za pomocą indeksu, wyszukiwanie odpowiedniego identyfikatora i innych danych z tabeli, oraz zatrzymaj się, gdy zostaną znalezione pierwsze 5 meczy. Podejrzewam, że zobaczysz bardzo różne wyniki dla tej kwerendy, w zależności od konkretnej wartości identyfikatora, którego używasz - jeśli identyfikator ma dużo ostatniej aktywności, wiersze powinny zostać znalezione bardzo szybko, ale jeśli nie, to skanowanie indeksu może trzeba zrobić o wiele więcej pracy.

W drugim przypadku uważam, że nie jest w stanie zastosować optymalizacji STOPKEY do najgłębszego bloku ze względu na dodatkową warstwę zagnieżdżania. W takim przypadku pełne skanowanie indeksu stałoby się znacznie mniej atrakcyjne, ponieważ zawsze musiałoby przeskanować cały indeks. Dlatego wybiera indeksowanie na id (zakładam), po którym następuje faktyczny sortowanie na dacie. Jeśli podana wartość id pasuje do małego podzbioru wierszy, prawdopodobnie będzie to bardziej wydajne - ale jeśli podasz wartość id, która ma wiele wierszy rozmieszczonych w całym tabeli, to spodziewam się, że będzie wolniejsza, ponieważ będzie uzyskać dostęp i sortować wiele wierszy.

Tak, przypuszczam, że twoje testy użyły wartości (wartości), które mają stosunkowo mało wierszy, które nie są zbyt niedawne.Jeśli byłby to typowy przypadek użycia, to drugie zapytanie prawdopodobnie jest lepsze dla ciebie (ponownie, z zastrzeżeniem, że nie jestem pewien, czy technicznie gwarantowane jest wygenerowanie poprawnego zestawu wyników). Ale jeśli typowe wartości będą miały większą liczbę pasujących wierszy i/lub bardziej prawdopodobne, że będą miały 5 bardzo aktualnych wierszy, wówczas pierwsze zapytanie i plan mogą być lepsze.

+0

Świetne wyjaśnienie! Dzięki. @Alex: wydaje się, że lepiej jest dodać 'order by rownum', ponieważ dodaje on polecenie" SORTUJ ZAMÓWIENIE PRZEZ STOPRKEY "w planie exec, podczas gdy aliasing' rownum' usuwa "COUNT STOPKEY" w planie exec. Ale, jak zauważyłeś, nie widziałem zmian prędkości. – Bazi