Làm thế nào để sử dụng Oracle ORDER BY và ROWNUM một cách chính xác?


126

Tôi đang gặp khó khăn khi chuyển đổi các thủ tục được lưu trữ từ SQL Server sang Oracle để sản phẩm của chúng tôi tương thích với nó.

Tôi có các truy vấn trả về bản ghi gần đây nhất của một số bảng, dựa trên dấu thời gian:

Máy chủ SQL:

SELECT TOP 1 *
FROM RACEWAY_INPUT_LABO
ORDER BY t_stamp DESC

=> Điều đó sẽ trả về cho tôi bản ghi gần đây nhất

Nhưng Oracle:

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

=> Điều đó sẽ trả về cho tôi bản ghi cũ nhất (có thể tùy thuộc vào chỉ số), bất kể ORDER BYcâu lệnh!

Tôi đã đóng gói truy vấn Oracle theo cách này để phù hợp với yêu cầu của tôi:

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

Và nó hoạt động. Nhưng nó giống như một vụ hack kinh khủng đối với tôi, đặc biệt là nếu tôi có rất nhiều bản ghi trong các bảng liên quan.

Cách tốt nhất để đạt được điều này là gì ?



4
Những gì bạn đã làm trong Truy vấn cuối cùng của mình là chính xác. Bạn chọn hàng đầu tiên của danh sách các bản ghi đã được sắp xếp. Đơn giản là đóng gói Truy vấn.
araknoid

1
Điều này được ghi rõ trong sách hướng dẫn: docs.oracle.com/cd/E11882_01/server.112/e26088/…
a_horse_with_no_name

5
@a_horse_with_no_name Ý của bạn là được ghi rõ ràng trong lỗi 404 này.
anthonybrice

3
@anthonybrice: cảm ơn. Oracle đã thay đổi tất cả các URL của họ sang hướng dẫn sử dụng. Liên kết cập nhật là: docs.oracle.com/cd/E11882_01/server.112/e41084/…
a_horse_with_no_name

Câu trả lời:


120

Các wheretuyên bố được thực hiện trước khi các order by. Vì vậy, truy vấn mong muốn của bạn là " lấy hàng đầu tiên và sau đó sắp xếp hàng theo mô t_stamp tả ". Và đó không phải là những gì bạn dự định.

Phương thức truy vấn con là phương pháp thích hợp để thực hiện việc này trong Oracle.

Nếu bạn muốn một phiên bản hoạt động trên cả hai máy chủ, bạn có thể sử dụng:

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

Bên ngoài *sẽ trả về "1" trong cột cuối cùng. Bạn cần liệt kê các cột riêng lẻ để tránh điều này.


40

Sử dụng ROW_NUMBER()thay thế. ROWNUMlà một cột giả và ROW_NUMBER()là một hàm. Bạn có thể đọc về sự khác biệt giữa chúng và xem sự khác biệt về đầu ra của các truy vấn dưới đây:

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
ROWNUMcó thể nhanh hơn ROW_NUMBER()nên hay không nên sử dụng cái này thay cho cái kia phụ thuộc vào một số yếu tố.
David Faber

Xin lỗi vì đã nhầm lẫn! Thật không may, tôi không thể lấy lại nó bây giờ.
Athafoud

0

Một thay thế mà tôi sẽ đề xuất trong trường hợp sử dụng này là sử dụng MAX (t_stamp) để nhận hàng mới nhất ... ví dụ:

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

Tùy chọn mẫu mã của tôi (có lẽ) - đáng tin cậy, thường hoạt động bằng hoặc tốt hơn so với việc cố gắng chọn hàng đầu tiên từ danh sách được sắp xếp - mục đích cũng dễ đọc hơn.
Hi vọng điêu nay co ich ...

SQLer


3
Không có GIỚI HẠN trong Oracle. Bạn đang cầu xin câu hỏi.
philipxy

0

Đã ghi lại một số vấn đề thiết kế với điều này trong một bình luận ở trên. Tóm lại, trong Oracle, bạn cần phải giới hạn kết quả theo cách thủ công khi bạn có các bảng lớn và / hoặc các bảng có cùng tên cột (và bạn không muốn nhập rõ ràng tất cả và đổi tên tất cả). Giải pháp dễ dàng là tìm ra điểm ngắt và giới hạn điểm đó trong truy vấn của bạn. Hoặc bạn cũng có thể làm điều này trong truy vấn bên trong nếu bạn không có ràng buộc tên cột xung đột. Ví dụ

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

sẽ cắt giảm đáng kể kết quả. Sau đó, bạn có thể ĐẶT HÀNG THEO hoặc thậm chí thực hiện truy vấn bên ngoài để giới hạn hàng.

Ngoài ra, tôi nghĩ TOAD có tính năng giới hạn hàng; nhưng, không chắc rằng điều đó không giới hạn trong truy vấn thực tế trên Oracle. Không chắc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.