¿Cómo utilizar Oracle ORDER BY y ROWNUM correctamente?


126

Me resulta difícil convertir los procedimientos almacenados de SQL Server a Oracle para que nuestro producto sea compatible con él.

Tengo consultas que devuelven el registro más reciente de algunas tablas, basado en una marca de tiempo:

Servidor SQL:

SELECT TOP 1 *
FROM RACEWAY_INPUT_LABO
ORDER BY t_stamp DESC

=> Eso me devolverá el registro más reciente

Pero Oracle:

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

=> ¡Eso me devolverá el registro más antiguo (probablemente según el índice), independientemente de la ORDER BYdeclaración!

Encapsulé la consulta de Oracle de esta manera para que coincida con mis requisitos:

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

y funciona. Pero me parece un truco horrible, especialmente si tengo muchos registros en las tablas involucradas.

Cuál es la mejor manera de lograr esto ?



44
Lo que has hecho en tu última consulta es correcto. Selecciona la primera fila de una lista ordenada de registros. Simplemente consulta encapsulación.
— aracnoideo

1
Esto está claramente documentado en el manual: docs.oracle.com/cd/E11882_01/server.112/e26088/…
— a_horse_with_no_name

55
@a_horse_with_no_name Quiere decir claramente documentado en este error 404.
— anthonybrice

3
@anthonybrice: gracias. Oracle cambió todas sus URL al manual. El enlace actualizado es: docs.oracle.com/cd/E11882_01/server.112/e41084/…
— a_horse_with_no_name

Respuestas:


120

La wheredeclaración se ejecuta antes de la order by. Entonces, su consulta deseada dice " tome la primera fila y luego t_stamp ordénela por desc ". Y eso no es lo que pretendes.

El método de subconsulta es el método adecuado para hacer esto en Oracle.

Si desea una versión que funcione en ambos servidores, puede usar:

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

El exterior *devolverá "1" en la última columna. Debería enumerar las columnas individualmente para evitar esto.


40

Usar en su ROW_NUMBER()lugar. ROWNUMes una pseudocolumna y ROW_NUMBER()es una función. Puede leer sobre la diferencia entre ellos y ver la diferencia en la salida de las consultas a continuación:

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

3
ROWNUMpodría ser más rápido que ROW_NUMBER()eso si uno debe usar uno sobre el otro depende de una serie de factores.
— David Faber

Disculpas por el voto negativo fue por error! Lamentablemente no puedo recuperarlo ahora.
— Athafoud

0

Una alternativa que sugeriría en este caso de uso es usar el MAX (t_stamp) para obtener la última fila ... por ejemplo

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

Mi preferencia de patrón de codificación (tal vez), confiable, generalmente funciona mejor o mejor que tratar de seleccionar la primera fila de una lista ordenada, también la intención es más explícitamente legible.
Espero que esto ayude ...

SQLer


3
No hay LÍMITE en Oracle. Estás rogando la pregunta.
— philipxy

0

Se documentaron algunos problemas de diseño con esto en un comentario anterior. En pocas palabras, en Oracle, debe limitar los resultados manualmente cuando tiene tablas grandes y / o tablas con los mismos nombres de columna (y no desea escribirlas explícitamente y cambiarles el nombre). La solución fácil es averiguar su punto de interrupción y limitarlo en su consulta. O también puede hacer esto en la consulta interna si no tiene la restricción de nombres de columna en conflicto. P.ej

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

reducirá sustancialmente los resultados. Luego puede ORDER BY o incluso hacer la consulta externa para limitar las filas.

Además, creo que TOAD tiene una función para limitar las filas; pero, no estoy seguro de que eso limite dentro de la consulta real en Oracle. No estoy seguro.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.