Jak mogę zdobyć pierwszą pozycję w Oracle?

251

Jak mam wykonać następujące czynności?

select top 1 Fname from MyTbl

W Oracle 11g ?

Złoto
źródło
3
Czy możesz nam powiedzieć, w jakiej kolejności chcesz znaleźć się na pierwszej pozycji?
Andrew Wolfe
1
Po pierwsze, nigdy nie powinieneś nigdy polegać na silniku DB. Jeśli chcesz wiedzieć takie rzeczy, włóż sekwencer. Gdy to zrobisz, gwarantuje się, że będą one ponumerowane w kolejności, w jakiej zostały wstawione.
FlyingGuy,
1
Bardzo przydatny materiał na ten temat use-the-index-luke.com/sql/partial-results/top-n-queries
Ilia Maskov

Odpowiedzi:

257

Jeśli chcesz tylko pierwszy wybrany wiersz, możesz:

select fname from MyTbl where rownum = 1

Możesz także użyć funkcji analitycznych, aby zamówić i wziąć górę x:

select max(fname) over (rank() order by some_factor) from MyTbl
Mcpeterson
źródło
54
Jest to dobre, jeśli chcesz tylko 1 wiersz i nie przejmuj się, które. Jeśli chcesz konkretnych wierszy, takich jak najnowszy rekord, musisz wykonać sortowanie w podselekcji, na przykład odpowiedź Vasha. Oracle przydziela rownum przed sortowaniem.
Scott Bailey,
4
@Scott yup. to jest poprawne. I Patrick, uważam, że składnia jest nieprawidłowa. To naprawdę powinno być utrzymanie (dense_rank () last ...)
mcpeterson
2
Różnica między pierwszym a drugim przykładem polega na tym, że pierwszy wybiera wiersz A (dowolny wiersz, bez kolejności). Drugi przykład pobiera wartość pierwszego wiersza bez wykonywania wewnętrznego zapytania dotyczącego kolejności (jak w przykładach poniżej).
JulesLt,
3
Składnia niepoprawna w: wybierz max (fname) ponad (rank () sortuj według some_factor) z MyTbl
Stéphane Gerber
1
@ jclozano „niezbyt dobrze znana funkcjonalność wyroczni” - Z szacunkiem zaczynam się różnić. Ma to znaczenie, ponieważ „mało znana funkcjonalność” zwykle sugeruje niejasność i dlatego może sugerować, że powinniśmy unikać używania. Nie jest to niejasne i nie należy go unikać.
Sepster
166
SELECT *
  FROM (SELECT * FROM MyTbl ORDER BY Fname )
 WHERE ROWNUM = 1;
Damian Leszczyński - Vash
źródło
8
Ta odpowiedź poprawnie pobiera wiersz TOP (porządkuje wyniki przed ograniczeniem do ROWNUM).
JulesLt,
Ta odpowiedź nie jest dokładnym tłumaczeniem - pierwotne zapytanie nie ma ORDER BY, ani nie zwraca wszystkich kolumn w tabeli.
Kucyki OMG,
Stoję skorygowany (patrz poniżej). Zamieni głosy po upływie czasu.
JulesLt,
7
@OMGPonies tak. ale prawdopodobnie tego właśnie chce większość ludzi, którzy odwiedzają tę stronę, przeglądając swój problem
NimChimpsky,
4
To z pewnością musi być zwycięska odpowiedź w tym wątku. Mógłbym dodać notatkę, że dla top Xjednego można to zmienić naWHERE ROWNUM <= X
Coś Co
31

Dzięki Oracle 12c (czerwiec 2013 r.) Możesz używać jej w następujący sposób.

SELECT * FROM   MYTABLE
--ORDER BY COLUMNNAME -OPTIONAL          
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
MSK
źródło
6
Ciekawe polecenie, używam tutaj 12c i OFFSET 0 ROWSnajwyraźniej nie jest to konieczne, możesz użyć, FETCH NEXT 1 ROWS ONLYa nawet FETCH FIRST ROW ONLY, kolejność według jest ważna lub będzie równoważna z samym użyciem WHERE rownum = 1. Próbowałem nawet w instrukcji OUTER APPLY i działało to jak funkcja TOP Ms-SQL.
Rafael Merlin,
Masz rację @RafaelMerlin. Po twoim poście uznałem, że OFFSET 0 ROWS nie jest konieczny. Przydałoby się to przy pobieraniu danych między górnym X a górnym Y.
MSK
1
Więcej przykładów: oracle-base.com/articles/12c/…
FixFaier
Jak dotąd tak dobrze, z ważnym brakującym punktem TIES. Odnosi się to do przypadków, w których występują powiązania dla wersji 12c +i12c -
Barbaros Özhan
10

Możesz użyć ROW_NUMBER()z ORDER BYklauzulą ​​w zapytaniu podrzędnym i użyć tej kolumny zamiast TOP N. Można to wyjaśnić krok po kroku.

Zobacz poniższą tabelę, która ma dwie kolumny NAMEi DT_CREATED.

wprowadź opis zdjęcia tutaj

Jeśli potrzebujesz wziąć tylko dwie pierwsze daty, niezależnie od tego NAME, możesz użyć poniższego zapytania. Logika została zapisana w zapytaniu

-- The number of records can be specified in WHERE clause
SELECT RNO,NAME,DT_CREATED
FROM
(
    -- Generates numbers in a column in sequence in the order of date
    SELECT ROW_NUMBER() OVER (ORDER BY DT_CREATED) AS RNO,
    NAME,DT_CREATED
    FROM DEMOTOP
)TAB
WHERE RNO<3;

WYNIK

wprowadź opis zdjęcia tutaj

W niektórych sytuacjach musimy wybrać TOP Nwyniki dla każdego z nich NAME. W takim przypadku możemy użyć PARTITION BYz ORDER BYklauzulą ​​w zapytaniu podrzędnym. Zobacz poniższe zapytanie.

-- The number of records can be specified in WHERE clause
SELECT RNO,NAME,DT_CREATED
FROM
(
  --Generates numbers in a column in sequence in the order of date for each NAME
    SELECT ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY DT_CREATED) AS RNO,
    NAME,DT_CREATED
    FROM DEMOTOP
)TAB
WHERE RNO<3;

WYNIK

wprowadź opis zdjęcia tutaj

Sarath Avanavu
źródło
1
Użycie ROW_NUMBER () ... jest bardziej poprawnym rozwiązaniem niż w temacie odpowiedzi. Jeden problem z tym rozwiązaniem (a także z wariantem maks. (Polowym)) polegający na tym, że nie można robić takich rzeczy jak „wybierz ... (wybierz ROW_NUMBER () ...) do aktualizacji ;”
Alexo Po.
I czasami jest to bardzo ważne w PL / SQL (przepraszam, nie edytowałem poprzedniego komentarza w ciągu 5 minut).
Alexo Po.
W takim przypadku możemy użyć CTE jak w części zewnętrznej. Dobrze? @Alexo Po.
Sarath Avanavu
Myślę, że cię nie rozumiem. klauzula aktualizacji może być używana, gdy ROWID jest „łatwo” zachowany przez Oracle. Tak więc grupowanie (i grupowanie z powodu użycia klauzul analitycznych) ukrywa prawdziwy ROWID i wierszy nie można zablokować. Po drugie, CTE ( with (select ... ) as klauzula) nic nie zmienia w tym problemie, CTE po prostu ma na celu czytanie i wspieranie zapytań. Dobrze? @Sarath Avanavu
Alexo Po.
Uwaga na siebie. Problem z ROWID faktycznie dzieje się szczególnie ze względu na to, gdzie warunek RNO <3 , w tym przypadku wartość RNO nie jest powiązana z ROWID, dlatego Oracle nie może blokować wierszy.
Alexo Po.
9

Możesz zrobić coś takiego

    SELECT *
      FROM (SELECT Fname FROM MyTbl ORDER BY Fname )
 WHERE rownum = 1;

Można również skorzystać z funkcji analitycznych RANK i / lub DENSE_RANK , ale ROWNUM jest chyba najprostszy.

Suman
źródło
1
czy możesz pomóc z jakimś przykładem rangi itp.
breakfreehg
9
select * from (
    select FName from MyTbl
)
where rownum <= 1;
a'r
źródło
5

Posługiwać się:

SELECT x.*
  FROM (SELECT fname 
          FROM MyTbl) x
 WHERE ROWNUM = 1

Jeśli używasz Oracle9i +, możesz spojrzeć na użycie funkcji analitycznych takich jak ROW_NUMBER (), ale nie będą one działały tak dobrze, jak ROWNUM .

Kucyki OMG
źródło
1
Dobra odpowiedź, ale zawiera małą literówkę. Gdzie mówisz, że Oracle9i + nie powinno być 8i? download-west.oracle.com/docs/cd/A87860_01/doc/server.817/…
Ian Carpenter
@carpenteri: To prawda, że ​​analityka była dostępna w 8i - nie pamiętam szczegółów, ale analityka nie była tak naprawdę publicznie dostępna do 9i.
OMG Kucyki
Mały komentarz - poniższa odpowiedź Vash zawiera ORDER BY w wewnętrznym zapytaniu, co jest krytyczne, jeśli chcesz NAJLEPSZĄ wartość fname, a nie „first” (która może być dowolna, najprawdopodobniej wstawiony pierwszy wiersz). Może warto edytować?
JulesLt,
@JulesLt: Kwerenda dostarczona przez OP nie zawiera ORDER BY, więc jest to odpowiedź i dokładne tłumaczenie na składnię Oracle.
Kucyki OMG,
Moje nieporozumienie dotyczące składni SQL SERVER TOP (błędnie zakładałem, że było podobne do FIRST w RANK, a nie ROWNUM). Zagłosowano.
JulesLt,
3

Aby wybrać pierwszy wiersz z tabeli i wybrać jeden wiersz z tabeli, są dwa różne zadania i wymagają innego zapytania. Można to zrobić na wiele sposobów. Cztery z nich to:

Pierwszy

select  max(Fname) from MyTbl;

druga

select  min(Fname) from MyTbl;

Trzeci

select  Fname from MyTbl  where rownum = 1;

Czwarty

select  max(Fname) from MyTbl where rowid=(select  max(rowid) from MyTbl)
Vikas Hardia
źródło
3

Miałem ten sam problem i mogę to naprawić za pomocą tego rozwiązania:

select a.*, rownum 
from (select Fname from MyTbl order by Fname DESC) a
where
rownum = 1

Możesz zamówić swój wynik wcześniej, aby mieć pierwszą wartość na górze.

Powodzenia

user2607028
źródło