Oracle SELECT TOP 10 rekordów


144

Mam duży problem z instrukcją SQL w Oracle. Chcę wybrać TOP 10 rekordów uporządkowanych przez STORAGE_DB, których nie ma na liście z innej instrukcji Select.

Ten działa dobrze dla wszystkich rekordów:

SELECT DISTINCT 
  APP_ID, 
  NAME, 
  STORAGE_GB, 
  HISTORY_CREATED, 
  TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE  
  FROM HISTORY WHERE 
      STORAGE_GB IS NOT NULL AND 
        APP_ID NOT IN (SELECT APP_ID
                       FROM HISTORY
                        WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') = '06.02.2009') 

Ale kiedy dodam

AND ROWNUM <= 10
ORDER BY STORAGE_GB DESC

Dostaję jakieś „przypadkowe” nagrania. Myślę, że ponieważ limit ma miejsce przed zamówieniem.

Czy ktoś ma dobre rozwiązanie? Inny problem: to zapytanie jest naprawdę powolne (ponad 10 000 rekordów)



Odpowiedzi:


199

Musisz umieścić swoje aktualne zapytanie w podzapytaniu, jak poniżej:

SELECT * FROM (
  SELECT DISTINCT 
  APP_ID, 
  NAME, 
  STORAGE_GB, 
  HISTORY_CREATED, 
  TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE  
  FROM HISTORY WHERE 
    STORAGE_GB IS NOT NULL AND 
      APP_ID NOT IN (SELECT APP_ID FROM HISTORY WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') ='06.02.2009')
  ORDER BY STORAGE_GB DESC )
WHERE ROWNUM <= 10

Oracle stosuje rownum do wyniku po jego zwróceniu.
Musisz przefiltrować wynik po jego zwróceniu, więc wymagane jest podzapytanie. Możesz również użyć funkcji RANK (), aby uzyskać najlepsze wyniki.

Aby uzyskać wydajność, spróbuj użyć NOT EXISTSzamiast NOT IN. Zobacz to po więcej.


NOT EXISTS nie działa w tym scenariuszu (nieprawidłowy operator relacyjny) APP_ID NOT EXISTS (SELEC ...)
opHASnoNAME

3
Niektórzy mogą powiedzieć, że może to zniechęcić ludzi do Oracle.
MrBoJangles

2
Sprawdź FETCH NEXT N ROWS ONLYodpowiedź poniżej.
Mohnish

@Padmarag: Kiedy rownum jest stosowane w zapytaniu takim jak to - wybierz * z SomeTable, gdzie someColumn = '123' i rownum <= 3. Czy to po wybraniu wyników z [Select * from SomeTable, gdzie someColumn = '123']
Shirgill Farhan,

55

Jeśli używasz Oracle 12c, użyj:

POBIERZ TYLKO NASTĘPNE N RZĘDÓW

SELECT DISTINCT 
  APP_ID, 
  NAME, 
  STORAGE_GB, 
  HISTORY_CREATED, 
  TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE  
  FROM HISTORY WHERE 
    STORAGE_GB IS NOT NULL AND 
      APP_ID NOT IN (SELECT APP_ID FROM HISTORY WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') ='06.02.2009')
  ORDER BY STORAGE_GB DESC
FETCH NEXT 10 ROWS ONLY

Więcej informacji: http://docs.oracle.com/javadb/10.5.3.0/ref/rrefsqljoffsetfetch.html


2
to jest złoto w porównaniu z inną odpowiedzią
aswzen

Zgadzam się z aswzen
Austin Springer

1
Chcę dać tej odpowiedzi 100 pozytywnych głosów! Ale niestety mam tylko jednego do nagrodzenia. Tak jest!
eidylon

23

Jeśli chodzi o słabą wydajność, może to być wiele rzeczy i to naprawdę powinno być osobną kwestią. Jest jednak jedna oczywista rzecz, która może stanowić problem:

WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') = '06.02.2009') 

Jeśli HISTORY_DATE naprawdę jest kolumną daty i jeśli ma indeks, to przepisanie będzie działać lepiej:

WHERE HISTORY_DATE = TO_DATE ('06.02.2009', 'DD.MM.YYYY')  

Dzieje się tak, ponieważ konwersja typu danych wyłącza użycie indeksu B-Tree.



11

Otrzymujesz pozornie losowy zestaw, ponieważ ROWNUM jest stosowane przed ORDER BY. Więc twoje zapytanie bierze pierwsze dziesięć wierszy i sortuje je.0 Aby wybrać dziesięć najwyższych pensji, powinieneś użyć funkcji analitycznej w podzapytaniu, a następnie odfiltrować to:

 select * from 
     (select empno,
             ename,
             sal,
             row_number() over(order by sal desc nulls last) rnm
    from emp) 
 where rnm<=10
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.