2009-05-05 10 views
11

Mam kwerendy SQL, który wygląda mniej więcej tak:Jak przyspieszyć numer wiersza w Oracle?

SELECT * FROM(
    SELECT 
     ..., 
     row_number() OVER(ORDER BY ID) rn 
    FROM 
     ... 
) WHERE rn between :start and :end 

Zasadniczo, jest to, że część ORDER BY jest spowolnienie rzeczy w dół. Jeśli miałbym go usunąć, koszt EXPLAIN spada o rząd wielkości (ponad 1000x). Próbowałem tego:

SELECT 
    ... 
FROM 
    ... 
WHERE 
    rownum between :start and :end 

Ale to nie daje poprawnych wyników. Czy jest jakiś łatwy sposób, aby to przyspieszyć? Czy będę musiał spędzić więcej czasu z narzędziem EXPLAIN?

Odpowiedz

12

ROW_NUMBER jest dość nieefektywny pod numerem Oracle.

Zobacz artykuł w moim blogu dla wydajności szczegóły:

dla konkretnego zapytania, polecam Ci go zastąpić ROWNUM i upewnij się, że indeks jest używany :

SELECT * 
FROM (
     SELECT /*+ INDEX_ASC(t index_on_column) NOPARALLEL_INDEX(t index_on_column) */ 
       t.*, ROWNUM AS rn 
     FROM table t 
     ORDER BY 
       column 
     ) 
WHERE rn >= :start 
     AND rownum <= :end - :start + 1 

To zapytanie użyje COUNT STOPKEY

Upewnij się również, że column nie jest zerowy, lub dodaj stan WHERE column IS NOT NULL.

W przeciwnym razie nie można użyć indeksu do pobrania wszystkich wartości.

Należy pamiętać, że nie można używać wartości ROWNUM BETWEEN :start and :end bez podzapytania.

jest zawsze przypisany jako ostatni i sprawdzany jako ostatni, w ten sposób ROWNUM zawsze jest w porządku bez luk.

Jeśli użyjesz , pierwszy wiersz, który spełnia wszystkie inne warunki, stanie się kandydatem do powrotu, tymczasowo przypisany z ROWNUM = 1 i nie powiedzie się testowi ROWNUM BETWEEN 10 AND 20.

Następnie następny wiersz będzie kandydatem, przypisanym z ROWNUM = 1 i nie powiedzie się itp., Więc w końcu żadne wiersze nie zostaną zwrócone.

To powinno być przetworzone przez umieszczenie ROWNUM w podkwerendie.

+0

Działa jak Jednak wskazówki dotyczące optymalizatora nie wydawały się mieć znaczącej różnicy: –

+3

Oznacza to, że 'CBO' był wystarczająco inteligentny, aby podnieść indeksy.To naprawdę było ROWNUM zamiast ROW_NUMBER, które miały tutaj znaczenie. – Quassnoi

+0

Ale nadal zostawiłbym wskazówki lub stworzył OUTLINE, na wypadek gdyby CBO zmieniło zdanie :) – Quassnoi

1

Czy Twoja kolumna ORDER BY jest indeksowana? Jeśli nie, to jest dobre miejsce na rozpoczęcie.

+0

W rzeczywistości tak nie było. Ale zmiana w wierszu indeksowanym przez IS nie pomaga. Dziękuję za zrobienie oczywistej sugestii. :-) –

+1

Indeks pomógłby tylko ulepszyć ORDER BY, jeśli ścieżka dostępu mogłaby korzystać z tego indeksu (np. Szukałeś zakresu identyfikatorów). –

0

Spędź więcej czasu dzięki narzędziu EXPLAIN PLAN. Jeśli widzisz SKANOWANIE TABELI, musisz zmienić swoje zapytanie.

Twoje zapytanie nie ma dla mnie większego sensu. Zapytanie o ROWID wydaje się być pytaniem o kłopoty. W tym zapytaniu nie ma żadnych informacji relacyjnych. Czy jest to prawdziwe zapytanie, z którym masz problem, lub przykład, który nadrobiłeś, aby zilustrować swój problem?

+0

To jest paginacja. Zasadniczo to pytanie dotyczy przynajmniej stronicowania. Właśnie wyjąłem resztę zapytania (głównie dlatego, że jest nietrywialne). Wszystkie elipsy są tam, gdzie wyciąłem materiał na zwięzłość. –

4

Wygląda na to zapytanie o paginację.

z tego ASKTOM artykułu (o 90% w dół strony):

You need to order by something unique for these pagination queries, so that ROW_NUMBER is assigned deterministically to the rows each and every time.

Również twoje pytania są nie w pobliżu ta sama, więc nie jestem pewien, co z korzyścią porównując koszty jeden do drugiego jest.

+1

Właściwie ten artykuł pomógł mi napisać zapytanie. Nie zauważyłem jednak części o zamówieniu przez unikalne identyfikatory. Istnieje również wskazówka optymalizatora zapytania, którą przegapiłem. Wypróbuję to jutro w pracy! –

+0

;) wydawało się, że wygląda znajomo. first_rows może być niesamowity w przypadku zapytań dotyczących stronicowania. – David

+0

Ta i Quassnoi rada dostała moje zapytanie do prawie stałego czasu! Chciałbym móc wybrać dwie odpowiedzi. :-( –

1

Część problemu polega na tym, jak duży jest "początek" do "końca" i miejsce, w którym "żyją". Powiedzmy, że masz milion wierszy w tabeli, a chcesz wierszy od 567,890 do 567,900, wtedy będziesz musiał żyć z tym, że będzie musiał przejść przez cały stół, sortując wszystko według ID i sprawdź, które rzędy mieszczą się w tym zakresie.

W skrócie, to dużo pracy, dlatego optymalizator zapewnia wysokie koszty.

Nie jest to też coś, z czym indeks może pomóc. Indeks dawałby zlecenie, ale w najlepszym przypadku daje ci to gdzie zacząć, a potem czytasz dalej, aż dojdziesz do 567,900 wpisów.

Jeśli wyświetlasz użytkownikowi końcowemu 10 przedmiotów na raz, może to być warte pobrania 100 z DB, a następnie podzielenia tej aplikacji na 100 części na dziesięć.

+0

To brzmi właściwie, naprawdę pobieram około 15 000 rekordów z ~ 2 milionów rekordów. Ograniczamy ilość czasu, jaki może wykonać zapytanie, a ściągnięcie wszystkich rekordów 15k naraz spowodowało przekroczenie limitu czasu. Myślę, że przeglądanie wyników uniemożliwiłoby to. Przypuszczam, że to oznacza, że ​​będę musiał przejść przez biurokratyczny koszmar z żądaniem dłuższego limitu czasu: –

+0

Mam nadzieję, że nie wysyłasz użytkownikowi 15,000 wierszy! –