2013-02-26 29 views
84

Tôi đang gặp khó khăn trong việc chuyển đổi các thủ tục lưu sẵn từ SQL Server sang Oracle để có sản phẩm tương thích với nó.Làm thế nào để sử dụng Oracle ORDER BY và ROWNUM chính xác?

Tôi có thắc mắc mà trả về kỷ lục mới nhất của một số bảng biểu, dựa trên một timestamp:

SQL Server:

SELECT TOP 1 * 
FROM RACEWAY_INPUT_LABO 
ORDER BY t_stamp DESC 

=> Đó là ý chí trả về cho tôi những kỷ lục 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ả lại cho tôi bản ghi cũ nhất (có thể tùy thuộc vào chỉ mục), bất kể tuyên bố ORDER BY!

tôi đóng gói truy vấn Oracle 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ó nghe có vẻ như là một bản hack khủng khiếp với tôi, đặc biệt là nếu tôi có 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ì?

+0

[On ROWNUM và Hạn chế Kết quả] (http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197. html) –

+4

Điều bạn đã làm trong Truy vấn cuối cùng 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 chỉ cần truy vấn đóng gói. – araknoid

+1

Điều này được ghi rõ trong tài liệu hướng dẫn: http://docs.oracle.com/cd/E11882_01/server.112/e26088/pseudocolumns009.htm#i1006297 –

Trả lời

84

Tuyên bố where được thực thi trướcorder by. Vì vậy, truy vấn mong muốn của bạn đang nói "lấy hàng đầu tiên và sau đó đặt hàng trướct_stampdesc". Và đó không phải là những gì bạn có ý định.

Phương pháp 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 có một phiên bản làm việc trong 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 

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

19

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

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 
+2

'ROWNUM' có thể nhanh hơn' ROW_NUMBER() 'để có hay không nên sử dụng một người khác phụ thuộc vào một số yếu tố. –

0

Đã nêu một vài vấn đề về thiết kế với nhận xét này ở trên. Câu chuyện ngắn, trong Oracle, bạn cần giới hạn các 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 loại bỏ tất cả chúng ra 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 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 kết quả đáng kể. Sau đó, bạn có thể ORDER BY 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 chắn rằng không giới hạn trong truy vấn thực tế trên Oracle.Không chắc.

-2

Chỉ cần sử dụng rownum như sau

select * 
from (select t.* 
     from raceway_input_labo ril 
     order by t_stamp desc 
    )  
where rownum = 1 
+0

Được thấy điều này đã được giảm xuống hai lần. Tôi biết giải pháp này không hoạt động tốt, nhưng tôi không biết tại sao. Có ai đó vui lòng giải thích lý do tại sao? Cảm ơn bạn. – JasonGabler

+1

@JasonGabler 't' phải là' ril'. Họ đã sao chép lỗi đánh máy đó từ câu trả lời được chấp nhận 3 năm sau đó. Hơn nữa, mã này luôn nằm trong câu hỏi. Không có giải thích về "như thế này", vì vậy nó không giải thích bất cứ điều gì. – philipxy

0

Một thay thế tôi sẽ đề nghị trong trường hợp sử dụng này là sử dụng MAX (t_stamp) để lấy 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ã hóa của tôi (có thể) - đáng tin cậy, thường hoạt động tốt hơn hoặc cố gắng chọn hàng thứ nhất từ ​​danh sách được sắp xếp - cũng có thể đọc rõ ràng hơn.
Hope this helps ...

SQLer

+0

Không có giới hạn trong Oracle. Bạn đang cầu xin câu hỏi. – philipxy

Các vấn đề liên quan