Jak prawidłowo korzystać z Oracle ORDER BY i ROWNUM?

126

Trudno mi jest przekształcić procedury składowane z SQL Server do Oracle, aby nasz produkt był z nim zgodny.

Mam zapytania, które zwracają najnowszy rekord niektórych tabel na podstawie sygnatury czasowej:

SQL Server:

SELECT TOP 1 *
FROM RACEWAY_INPUT_LABO
ORDER BY t_stamp DESC

=> To zwróci mi najnowszy rekord

Ale Oracle:

SELECT *
FROM raceway_input_labo 
WHERE  rownum <= 1
ORDER BY t_stamp DESC

=> To zwróci mi najstarszy rekord (prawdopodobnie w zależności od indeksu), niezależnie od ORDER BYinstrukcji!

Zapytanie Oracle zawarłem w taki sposób, aby spełniało moje wymagania:

SELECT * 
FROM 
    (SELECT *
     FROM raceway_input_labo 
     ORDER BY t_stamp DESC)
WHERE  rownum <= 1

i to działa. Ale brzmi to dla mnie jak okropny hack, zwłaszcza jeśli mam dużo rekordów w powiązanych tabelach.

Jaki jest najlepszy sposób, aby to osiągnąć?

Larry
źródło
4
To, co zrobiłeś w ostatnim zapytaniu, jest poprawne. Wybierasz pierwszy wiersz uporządkowanej listy rekordów. Po prostu enkapsulacja zapytań.
araknoid
1
Jest to wyraźnie udokumentowane w podręczniku: docs.oracle.com/cd/E11882_01/server.112/e26088/ ...
a_horse_with_no_name
5
@a_horse_with_no_name Masz na myśli jasno udokumentowane w tym błędzie 404.
anthonybrice
3
@anthonybrice: dzięki. Firma Oracle zmieniła wszystkie swoje adresy URL na podręcznik. Aktualne łącze to: docs.oracle.com/cd/E11882_01/server.112/e41084/…
a_horse_with_no_name

Odpowiedzi:

120

whereOświadczenie zostanie wykonany przedorder by . Zatem żądane zapytanie brzmi „ weź pierwszy wiersz, a następnie uporządkuj go według t_stamp opisu ”. A nie tego zamierzasz.

Metoda podzapytania jest właściwą metodą w Oracle.

Jeśli chcesz wersję, która działa na obu serwerach, możesz użyć:

select ril.*
from (select ril.*, row_number() over (order by t_stamp desc) as seqnum
      from raceway_input_labo ril
     ) ril
where seqnum = 1

Zewnętrzny *zwróci „1” w ostatniej kolumnie. Aby tego uniknąć, należałoby wymienić poszczególne kolumny.

Gordon Linoff
źródło
40

Użyj ROW_NUMBER()zamiast tego. ROWNUMjest pseudokolumną i ROW_NUMBER()jest funkcją. Możesz przeczytać o różnicy między nimi i zobaczyć różnicę w wynikach poniższych zapytań:

SELECT * FROM (SELECT rownum, deptno, ename
           FROM scott.emp
        ORDER BY deptno
       )
 WHERE rownum <= 3
 /

ROWNUM    DEPTNO    ENAME
---------------------------
 7        10    CLARK
 14       10    MILLER
 9        10    KING


 SELECT * FROM 
 (
  SELECT deptno, ename
       , ROW_NUMBER() OVER (ORDER BY deptno) rno
  FROM scott.emp
 ORDER BY deptno
 )
WHERE rno <= 3
/

DEPTNO    ENAME    RNO
-------------------------
10    CLARK        1
10    MILLER       2
10    KING         3
Sztuka
źródło
3
ROWNUMmoże być szybsze niż ROW_NUMBER()to, czy należy użyć jednego nad drugim, zależy od wielu czynników.
David Faber,
Przepraszamy za głos przeciw, to był błąd! Niestety nie mogę tego teraz cofnąć.
Athafoud
0

Alternatywą, którą sugerowałbym w tym przypadku, jest użycie MAX (t_stamp), aby uzyskać najnowszy wiersz ... np

select t.* from raceway_input_labo t
where t.t_stamp = (select max(t_stamp) from raceway_input_labo) 
limit 1

Moje preferencje dotyczące wzorca kodowania (być może) - niezawodne, generalnie działają na poziomie lub lepiej niż próba wybrania pierwszego wiersza z posortowanej listy - również zamiar jest bardziej czytelny.
Mam nadzieję że to pomoże ...

SQLer

SQLer
źródło
3
W Oracle nie ma LIMITU. Błagasz o to pytanie.
philipxy
0

W powyższym komentarzu udokumentowano kilka problemów projektowych z tym związanych. Krótko mówiąc, w Oracle musisz ręcznie ograniczyć wyniki, jeśli masz duże tabele i / lub tabele z takimi samymi nazwami kolumn (i nie chcesz jawnie wpisywać ich wszystkich i zmieniać ich nazw). Prostym rozwiązaniem jest ustalenie punktu przerwania i ograniczenie go w zapytaniu. Możesz też to zrobić w zapytaniu wewnętrznym, jeśli nie masz ograniczenia dotyczącego nazw kolumn powodujących konflikt. Na przykład

WHERE m_api_log.created_date BETWEEN TO_DATE('10/23/2015 05:00', 'MM/DD/YYYY HH24:MI') 
                                 AND TO_DATE('10/30/2015 23:59', 'MM/DD/YYYY HH24:MI')  

znacznie ograniczy wyniki. Następnie możesz ORDER BY lub nawet wykonać zapytanie zewnętrzne, aby ograniczyć liczbę wierszy.

Myślę też, że TOAD ma funkcję ograniczania wierszy; ale nie jestem pewien, czy to ogranicza w ramach rzeczywistego zapytania w Oracle. Niepewny.

max Weber
źródło