2009-09-11 8 views
7

Mam bazę danych oracle wypełnioną milionami rekordów. Próbuję napisać zapytanie SQL, która zwraca pierwszy „N” sortowanie zapisy (powiedzmy 100 rekordów) z bazy danych na podstawie określonego warunku.Jak wybrać pierwsze rekordy "N" z bazy danych zawierającej miliony rekordów?

SELECT * 
FROM myTable 
Where SIZE > 2000 
ORDER BY NAME DESC 

Następnie programowo wybrać pierwsze rekordy n.

Problem z tego podejścia jest:

  • wyniki zapytań na pół miliona ewidencji i „Zamów przez nazwę” powoduje wszystkie rekordy mają być sortowane na nazwisko w kolejności malejącej Ten sortowania bierze dużo czasu (prawie.. 30-40 sekund ds. Jeśli pominę ORDER BY, zajmie to tylko 1 sekundę).
  • Po sortowaniu interesuje mnie tylko pierwsze N ​​(100) rekordów. Więc sortowanie kompletnych rekordów nie jest użyteczne.

Moje pytania są następujące:

  1. Czy możliwe jest określenie 'n' w samej zapytania? (więc sortowanie dotyczy tylko N rekordów, a zapytanie staje się szybsze).
  2. Lepszy sposób w SQL w celu ulepszenia zapytania do sortowania tylko elementów i powrotu w szybki czas .

Odpowiedz

19

Jeśli Twoim celem jest znalezienie 100 losowych rzędów i posortowanie ich później, to Lasse's solution jest poprawne. Jeśli tak myślę chcesz pierwszych 100 wierszy posortowanych według nazwy, odrzucając pozostałe byś zbudować kwerendę tak:

SELECT * 
    FROM (SELECT * 
      FROM myTable 
     WHERE SIZE > 2000 ORDER BY NAME DESC) 
WHERE ROWNUM <= 100 

optymalizator będzie zrozumieć, że jest to TOP-N zapytań i będą mogli korzystać indeks na NAME.Nie będzie musiał sortować całego zestawu wyników, zacznie tylko od początku indeksu i odczytywał go wstecz i zatrzymywał po 100 wierszach.

Możesz również dodać podpowiedź do pierwotnego zapytania, aby optymalizator zrozumiał, że interesują Cię tylko pierwsze wiersze. To prawdopodobnie będzie generować podobną ścieżkę dostępu:

SELECT /*+ FIRST_ROWS*/* FROM myTable WHERE SIZE > 2000 ORDER BY NAME DESC 

Edit: prostu dodając AND rownum <= 100 do zapytania nie będzie działać, ponieważ w Oracle rownum jest przypisany przed sortowania: dlatego trzeba użyć podzapytania . Bez podzapytania Oracle wybierze 100 losowych wierszy, a następnie je posortuje.

+0

Niesamowite, działa! – Oliver

4

Dodaj to:

AND rownum <= 100 

do Where-klauzuli.

Jednak nie zrobi to, o co prosisz.

Jeśli chcesz wybrać 100 losowych wierszy, posortować je, a następnie je zwrócić, musisz sformułować zapytanie bez ORDER BY, a następnie ograniczyć do 100 wierszy, a następnie wybrać z niego i sortować.

Ten mógłby praca, ale niestety nie mam serwera Oracle dostępne do badania:

SELECT * 
FROM (
    SELECT * 
    FROM myTable 
    WHERE SIZE > 2000 
     AND rownum <= 100 
    ) x 
ORDER BY NAME DESC 

jednak pamiętać, że „random” część tam, mówisz „daj mi 100 wierszy z ROZMIAR> 2000, nie obchodzi mnie, które 100 ".

Czy to naprawdę chcesz?

I nie, tak naprawdę nie otrzymasz losowego wyniku w tym sensie, że zmieni się za każdym razem, gdy zapytasz serwer, ale jesteś na łasce optymalizatora zapytań. Jeśli statystyki ładowania danych i indeksów dla tej tabeli zmieniają się w czasie, w pewnym momencie możesz uzyskać inne dane niż w poprzednim zapytaniu.

+0

Dzięki za odpowiedź. Moje zapytanie nie jest losowe 100. Chcę uzyskać pierwsze 100 posortowanych rekordów. Na przykład: jeśli zapisy są 1,5,8,2,1, 2,3,6,7. A jeśli chcę mieć 3 rekordy, odpowiedź byłaby następująca: (1,2,3) –

+2

Wtedy ty * chcesz * najpierw je posortować, a jeśli posortowanie miliona wierszy zajmuje dużo czasu, to niewiele pomoże. Jedyne, co robisz, to unikanie pobierania wszystkich wierszy w sieci, sortowanie musi jeszcze zostać uruchomione. –

+1

Jednak Oracle jest wystarczająco inteligentny, aby utrzymać 100 najlepszych wyników. Jeśli następny wiersz znajduje się poza tym 100, odrzuca go. W ten sposób nie musi sortować całej rzeczy. To jest O (n) zamiast O (n log n) –

5

This pokazuje, jak wybrać górne N wierszy w zależności od wersji Oracle.

z Oracle 9i r RANK() i DENSE_RANK(), funkcje mogą być wykorzystane do określenia, wiersze góry N. Przykłady:

uzyskać top 10 pracowników w oparciu o wynagrodzenia

SELECT ename, Sal FROM (SELECT ename, Sal, RANK() OVER (ORDER BY Sal DESC) sal_rank FROM emp) Gdzie sal_rank < = 10;

Wybierz pracownikom podejmowania top 10 pensje

SELECT ename, Sal FROM (SELECT ename, Sal, DENSE_RANK() OVER (ORDER od SAL DESC) sal_dense_rank FROM emp) Gdzie sal_dense_rank < = 10 ;

Różnica między nimi jest wyjaśnione here

0

Twoim problemem jest to, że porządek jest wykonywana za każdym razem, gdy kwerenda jest uruchamiany. Operację sortowania można wyeliminować za pomocą indeksu - optymalizator może użyć indeksu do wyeliminowania operacji sortowania - jeśli posortowana kolumna zostanie zadeklarowana jako NIE NULL.

(Jeśli kolumna jest możliwa do zniesienia, jest nadal możliwe, przez (a) dodanie kwerendy NOT NULL do zapytania lub (b) dodanie indeksu opartego na funkcjach i odpowiednią modyfikację klauzuli ORDER BY).

0

Dla odniesienia, w Oracle 12c, zadanie to można wykonać przy użyciu klauzuli FETCH. Możesz zobaczyć here dla przykładów i dodatkowe linki referencyjne dotyczące tej kwestii.

Powiązane problemy