Oracle ORDER BY 및 ROWNUM을 올바르게 사용하는 방법은 무엇입니까?


126

저장 프로 시저를 SQL Server에서 Oracle로 변환하여 제품과 호환되도록하는 데 어려움을 겪고 있습니다.

타임 스탬프를 기반으로 일부 테이블의 최신 레코드를 반환하는 쿼리가 있습니다.

SQL 서버 :

SELECT TOP 1 *
FROM RACEWAY_INPUT_LABO
ORDER BY t_stamp DESC

=> 가장 최근 기록을 반환합니다.

그러나 Oracle :

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

=> ORDER BY문에 상관없이 가장 오래된 레코드를 반환합니다 (아마도 인덱스에 따라 다름) !

내 요구 사항에 맞게 Oracle 쿼리를 다음과 같이 캡슐화했습니다.

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

그리고 그것은 작동합니다. 그러나 그것은 나에게 끔찍한 해킹처럼 들리는데, 특히 관련 테이블에 많은 레코드가있는 경우 더욱 그렇습니다.

이것을 달성하는 가장 좋은 방법은 무엇입니까?



4
마지막 쿼리에서 수행 한 작업이 정확합니다. 정렬 된 레코드 목록의 첫 번째 행을 선택합니다. 간단히 쿼리 캡슐화.
araknoid

1
이는 설명서에 명확하게 설명되어 있습니다. docs.oracle.com/cd/E11882_01/server.112/e26088/…
a_horse_with_no_name 2013

5
@a_horse_with_no_name이 404 오류에 명확하게 문서화되어 있음을 의미합니다.
anthonybrice

3
@anthonybrice : 감사합니다. 오라클은 모든 URL을 매뉴얼로 변경했습니다. 최신 링크 : docs.oracle.com/cd/E11882_01/server.112/e41084/…
a_horse_with_no_name

답변:


120

where문이 실행됩니다 전에order by . 따라서 원하는 쿼리는 " 첫 번째 행을 가져 와서 t_stamp desc 로 정렬 "이라고 말합니다. 그리고 그것은 당신이 의도 한 것이 아닙니다.

하위 쿼리 방법은 Oracle에서이를 수행하는 데 적합한 방법입니다.

두 서버 모두에서 작동하는 버전을 원하는 경우 다음을 사용할 수 있습니다.

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

바깥 쪽 *은 마지막 열에서 "1"을 반환합니다. 이를 방지하려면 열을 개별적으로 나열해야합니다.


40

ROW_NUMBER()대신 사용하십시오 . ROWNUM의사 열이며 ROW_NUMBER()함수입니다. 그들 사이의 차이점에 대해 읽고 아래 쿼리의 출력 차이를 볼 수 있습니다.

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
ROWNUMROW_NUMBER()중 하나를 사용해야하는지 여부는 여러 요인에 따라 달라집니다.
David Faber 2015

실수로 투표 한 것에 대해 사과드립니다! 불행히도 지금은 되돌릴 수 없습니다.
Athafoud

0

이 사용 사례에서 제안 할 대안은 MAX (t_stamp)를 사용하여 최신 행을 가져 오는 것입니다.

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

내 코딩 패턴 선호도 (아마도)-신뢰할 수 있고, 일반적으로 정렬 된 목록에서 첫 번째 행을 선택하는 것보다 더 나은 성능을 발휘합니다. 또한 의도가 더 명시 적으로 가독성이 높습니다.
도움이 되었기를 바랍니다 ...

SQLer


3
Oracle에는 제한이 없습니다. 당신은 질문을 구걸하고 있습니다.
philipxy

0

위의 주석에서 이와 관련된 몇 가지 디자인 문제를 문서화했습니다. 짧은 이야기, Oracle에서는 큰 테이블 및 / 또는 동일한 열 이름을 가진 테이블이있을 때 결과를 수동으로 제한해야합니다 (그리고 모두 명시 적으로 입력하고 이름을 바꾸고 싶지는 않음). 쉬운 해결책은 중단 점을 파악하고 쿼리에서이를 제한하는 것입니다. 또는 충돌하는 열 이름 제약 조건이없는 경우 내부 쿼리에서이 작업을 수행 할 수도 있습니다. 예

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

결과가 크게 줄어들 것입니다. 그런 다음 ORDER BY 또는 외부 쿼리를 수행하여 행을 제한 할 수 있습니다.

또한 TOAD에는 행을 제한하는 기능이 있다고 생각합니다. 그러나 그것이 Oracle의 실제 쿼리 내에서 제한되는지 확실하지 않습니다. 확실하지 않다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.