Wie verwende ich Oracle ORDER BY und ROWNUM richtig?

126

Es fällt mir schwer, gespeicherte Prozeduren von SQL Server auf Oracle zu konvertieren, damit unser Produkt damit kompatibel ist.

Ich habe Abfragen, die den neuesten Datensatz einiger Tabellen basierend auf einem Zeitstempel zurückgeben:

SQL Server:

SELECT TOP 1 *
FROM RACEWAY_INPUT_LABO
ORDER BY t_stamp DESC

=> Damit erhalte ich den neuesten Datensatz

Aber Oracle:

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

=> Damit erhalte ich den ältesten Datensatz (wahrscheinlich abhängig vom Index), unabhängig von der ORDER BYAussage!

Ich habe die Oracle-Abfrage so gekapselt, dass sie meinen Anforderungen entspricht:

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

und es funktioniert. Aber es klingt für mich nach einem schrecklichen Hack, besonders wenn ich viele Datensätze in den beteiligten Tabellen habe.

Was ist der beste Weg, um dies zu erreichen?

Larry
quelle
4
Was Sie in Ihrer letzten Abfrage getan haben, ist korrekt. Sie wählen die erste Zeile einer geordneten Liste von Datensätzen aus. Einfach die Kapselung abfragen.
Araknoid
1
Dies ist im Handbuch klar dokumentiert: docs.oracle.com/cd/E11882_01/server.112/e26088/…
a_horse_with_no_name
5
@a_horse_with_no_name Sie meinen, in diesem 404-Fehler klar dokumentiert.
Anthony Brice
3
@anthonybrice: danke. Oracle hat alle URLs in das Handbuch geändert. Der aktuelle Link lautet: docs.oracle.com/cd/E11882_01/server.112/e41084/…
a_horse_with_no_name

Antworten:

120

Die whereAnweisung wird vor dem ausgeführt order by. Ihre gewünschte Abfrage lautet also " Nimm die erste Zeile und t_stamp ordne sie dann nach absteigend ". Und das haben Sie nicht vor.

Die Unterabfragemethode ist die richtige Methode, um dies in Oracle zu tun.

Wenn Sie eine Version wünschen, die auf beiden Servern funktioniert, können Sie Folgendes verwenden:

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

Das Äußere *gibt "1" in der letzten Spalte zurück. Sie müssten die Spalten einzeln auflisten, um dies zu vermeiden.

Gordon Linoff
quelle
40

Verwenden Sie ROW_NUMBER()stattdessen. ROWNUMist eine Pseudospalte und ROW_NUMBER()ist eine Funktion. Sie können den Unterschied zwischen ihnen nachlesen und den Unterschied in der Ausgabe der folgenden Abfragen sehen:

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
Kunst
quelle
3
ROWNUMEs könnte schneller sein ROW_NUMBER(), ob einer über den anderen verwendet werden soll oder nicht, hängt von einer Reihe von Faktoren ab.
David Faber
Entschuldigung für die Ablehnung war es versehentlich! Leider kann ich es jetzt nicht zurücknehmen.
Athafoud
0

Eine Alternative, die ich in diesem Anwendungsfall vorschlagen würde, ist die Verwendung von MAX (t_stamp), um die neueste Zeile abzurufen ... z

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

Meine Codierungsmusterpräferenz (vielleicht) - zuverlässig, im Allgemeinen leistungsfähiger oder besser als der Versuch, die erste Zeile aus einer sortierten Liste auszuwählen - auch die Absicht ist expliziter lesbar.
Hoffe das hilft ...

SQLer

SQLer
quelle
3
Es gibt kein LIMIT in Oracle. Sie stellen die Frage.
philipxy
0

Dokumentierte einige Designprobleme damit in einem Kommentar oben. Kurz gesagt, in Oracle müssen Sie die Ergebnisse manuell einschränken, wenn Sie große Tabellen und / oder Tabellen mit denselben Spaltennamen haben (und Sie möchten nicht alle explizit eingeben und alle umbenennen). Eine einfache Lösung besteht darin, Ihren Haltepunkt herauszufinden und diesen in Ihrer Abfrage zu begrenzen. Sie können dies auch in der inneren Abfrage tun, wenn Sie nicht die Einschränkung für widersprüchliche Spaltennamen haben. Z.B

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')  

wird die Ergebnisse erheblich reduzieren. Dann können Sie ORDER BY oder sogar die äußere Abfrage durchführen, um Zeilen zu begrenzen.

Ich denke auch, dass TOAD eine Funktion zum Begrenzen von Zeilen hat. Ich bin mir jedoch nicht sicher, ob dies die tatsächliche Abfrage unter Oracle einschränkt. Nicht sicher.

maxweber
quelle