Phân trang với Oracle


97

Tôi không quen thuộc với Oracle như tôi muốn. Tôi có khoảng 250 nghìn bản ghi và tôi muốn hiển thị 100 bản ghi trên mỗi trang. Hiện tại, tôi có một thủ tục được lưu trữ truy xuất tất cả một phần tư triệu bản ghi vào tập dữ liệu bằng cách sử dụng bộ điều hợp dữ liệu và tập dữ liệu cũng như phương thức dataadapter.Fill (tập dữ liệu) trên các kết quả từ proc được lưu trữ. Nếu tôi có "Số trang" và "Số bản ghi trên mỗi trang" dưới dạng giá trị số nguyên mà tôi có thể chuyển dưới dạng tham số, thì cách tốt nhất để lấy lại phần cụ thể đó là gì. Giả sử, nếu tôi chuyển 10 dưới dạng số trang và 120 là số trang, từ câu lệnh select, nó sẽ cho tôi thứ 1880 đến 1200, hoặc tương tự như vậy, phép toán trong đầu của tôi có thể bị tắt.

Tôi đang làm điều này trong .NET với C #, nghĩ rằng điều đó không quan trọng, nếu tôi có thể thực hiện nó ngay trên mặt sql, thì tôi sẽ rất tuyệt.

Cập nhật: Tôi đã có thể sử dụng đề xuất của Brian và nó đang hoạt động rất tốt. Tôi muốn làm việc trên một số tối ưu hóa, nhưng các trang sẽ xuất hiện sau 4 đến 5 giây chứ không phải một phút và điều khiển phân trang của tôi có thể tích hợp rất tốt với các tài liệu lưu trữ mới của tôi.

Câu trả lời:


144

Một cái gì đó như thế này sẽ hoạt động: Từ Blog của Frans Bouma

SELECT * FROM
(
    SELECT a.*, rownum r__
    FROM
    (
        SELECT * FROM ORDERS WHERE CustomerID LIKE 'A%'
        ORDER BY OrderDate DESC, ShippingDate DESC
    ) a
    WHERE rownum < ((pageNumber * pageSize) + 1 )
)
WHERE r__ >= (((pageNumber-1) * pageSize) + 1)

4
Có, đó là cột 'tích hợp sẵn' mà Oracle hỗ trợ, nó luôn bắt đầu từ 1 và tăng dần cho mỗi hàng. Vì vậy, trong đoạn mã này, nếu bạn có 1000 hàng, thứ tự sắp xếp sẽ được áp dụng và sau đó mỗi hàng được gán một rownum. (Các) lựa chọn bên ngoài sử dụng các số hàng đó để xác định 'trang' bạn đang tìm kiếm dựa trên kích thước trang của bạn.
Brian Schmitt,

9
Điều này rất hay, nhưng chậm kinh khủng khi chọn số lượng lớn, chỉ cần kiểm tra xem đâu sẽ là thời điểm chọn 0 đến 1000 và 500.000 đến 501.000 ... Tôi đang sử dụng loại cấu trúc chọn này và tôi đang tìm cách giải quyết.
newhouse

3
@ n3whous3 bạn có thể thử này - inf.unideb.hu/~gabora/pagination/results.html
jasonk

7
Tôi tự hỏi tại sao hai WHEREkhông thể được kết hợp với AND, và sau đó tìm thấy này: orafaq.com/wiki/ROWNUM
Mengdi Gao

1
Phân trang Oracle làm hỏng ngày của tôi.
Aetherus

134

Hỏi Tom về phân trang và các hàm phân tích rất, rất hữu ích.

Đây là đoạn trích từ trang đó:

select * from (
    select /*+ first_rows(25) */
     object_id,object_name,
     row_number() over
    (order by object_id) rn
        from all_objects)
    where rn between :n and :m
        order by rn;

7
Đây thực sự là một triển khai tốt hơn nhiều, mặc dù rất khó tìm thấy trên bài đăng đó. Khi bạn có nhiều trang lớn, câu trả lời khác cũng phải đi qua tất cả các hàng từ các trang trước. Trong các truy vấn phức tạp, điều này có nghĩa là các trang sau hoạt động kém hơn các trang trước đó.
highseth

@tallseth Bạn nói đúng. Thật khó để tìm thấy nó trên trang đó. Đoạn trích được thêm vào.
Chobicus

Đây là câu trả lời chính xác nếu bạn muốn tự động thay đổi đơn đặt hàng của mình.
chakeda

74

Vì lợi ích của sự hoàn chỉnh, đối với những người đang tìm kiếm một giải pháp hiện đại hơn, trong Oracle 12c có một số tính năng mới bao gồm phân trang tốt hơn và xử lý hàng đầu.

Phân trang

Phân trang trông như thế này:

SELECT *
FROM user
ORDER BY first_name
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY;

Bản ghi N hàng đầu

Nhận các bản ghi hàng đầu trông như thế này:

SELECT *
FROM user
ORDER BY first_name
FETCH FIRST 5 ROWS ONLY

Lưu ý rằng cả hai ví dụ truy vấn trên đều có ORDER BYmệnh đề. Các lệnh mới tôn trọng những điều này và được chạy trên dữ liệu được sắp xếp.

Tôi không thể tìm thấy trang tham khảo Oracle tốt cho FETCHhoặc OFFSETnhưng trang này có tổng quan tuyệt vời về các tính năng mới này.

Hiệu suất

Như @wweicker đã chỉ ra trong các nhận xét bên dưới, hiệu suất là một vấn đề với cú pháp mới trong 12c. Tôi không có bản sao của 18c để kiểm tra xem Oracle đã cải thiện nó hay chưa.

Điều thú vị là, kết quả thực tế của tôi được trả lại nhanh hơn một chút vào lần đầu tiên tôi chạy các truy vấn trên bảng của mình (113 triệu + hàng) cho phương pháp mới:

  • Phương pháp mới: 0,013 giây.
  • Phương pháp cũ: 0,107 giây.

Tuy nhiên, như @wweicker đã đề cập, phương án giải thích có vẻ tệ hơn nhiều đối với phương pháp mới:

  • Chi phí phương pháp mới: 300.110
  • Chi phí phương pháp cũ: 30

Cú pháp mới đã thực hiện quét toàn bộ chỉ mục trên cột của tôi, đó là toàn bộ chi phí. Rất có thể, mọi thứ trở nên tồi tệ hơn nhiều khi giới hạn dữ liệu chưa lập chỉ mục.

Hãy xem xét khi bao gồm một cột chưa lập chỉ mục duy nhất trên tập dữ liệu trước đó:

  • Thời gian / chi phí của phương pháp mới: 189,55 giây / 998.908
  • Thời gian / chi phí phương pháp cũ: 1.973 giây / 256

Tóm tắt: sử dụng thận trọng cho đến khi Oracle cải thiện việc xử lý này. Nếu bạn có một chỉ mục để làm việc, có lẽ bạn có thể sử dụng phương pháp mới.

Hy vọng rằng tôi sẽ sớm có một bản sao của 18c để chơi và có thể cập nhật


Đây là câu trả lời tuyệt vời cho người dùng 12c
Lalji Gajera 14/02/19

1
Cú pháp sạch hơn, nhưng hiệu suất là tồi tệ hơn ( dba-presents.com/index.php/databases/oracle/... )
wweicker

Thật tốt khi biết, cảm ơn @wweicker. Hy vọng rằng hiệu suất sẽ sớm được Oracle khắc phục; mặc dù biết Oracle, đây có thể là một hy vọng xa vời!
JoelC

Cú pháp mới và nó được chuyển đổi thành lệnh gọi ROW_NUMBER / RANK thông thường. Liên quan Làm cách nào để giới hạn số hàng được trả về bởi một truy vấn Oracle sau khi đặt hàng?
Lukasz Szozda

@JoelC có bất kỳ thay đổi nào đối với ý kiến ​​của bạn không?
Ryan

11

Chỉ muốn tóm tắt các câu trả lời và nhận xét. Có một số cách để phân trang.

Trước oracle 12c, không có chức năng OFFSET / FETCH, vì vậy hãy xem whitepaper như @jasonk đã đề xuất. Đây là bài viết đầy đủ nhất mà tôi tìm thấy về các phương pháp khác nhau với giải thích chi tiết về ưu và nhược điểm. Sẽ mất một khoảng thời gian đáng kể để sao chép và dán chúng ở đây, vì vậy tôi sẽ không làm điều đó.

Ngoài ra còn có một bài viết hay từ những người sáng tạo jooq giải thích một số lưu ý phổ biến với oracle và phân trang cơ sở dữ liệu khác. jooq's blogpost

Tin tốt, kể từ oracle 12c, chúng tôi có một chức năng OFFSET / FETCH mới. Các tính năng mới của OracleMagazine 12c . Vui lòng tham khảo "Truy vấn Top-N và Phân trang"

Bạn có thể kiểm tra phiên bản oracle của mình bằng cách đưa ra câu lệnh sau

SELECT * FROM V$VERSION

7

Hãy thử những cách sau:

SELECT *
FROM
  (SELECT FIELDA,
    FIELDB,
    FIELDC,
    ROW_NUMBER() OVER (ORDER BY FIELDC) R
  FROM TABLE_NAME
  WHERE FIELDA = 10
  )
WHERE R >= 10
AND R   <= 15;

qua [tecnicume]


0

Trong dự án của mình, tôi đã sử dụng Oracle 12c và java . Mã phân trang trông giống như sau:

 public public List<Map<String, Object>> getAllProductOfferWithPagination(int pageNo, int pageElementSize, Long productOfferId, String productOfferName) {
    try {

        if(pageNo==1){
            //do nothing
        } else{
            pageNo=(pageNo-1)*pageElementSize+1;
        }
        System.out.println("algo pageNo: " + pageNo +"  pageElementSize: "+ pageElementSize+"  productOfferId: "+ productOfferId+"  productOfferName: "+ productOfferName);

        String sql = "SELECT * FROM ( SELECT * FROM product_offer po WHERE po.deleted=0 AND (po.product_offer_id=? OR po.product_offer_name LIKE ? )" +
             " ORDER BY po.PRODUCT_OFFER_ID asc) foo OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ";

       return jdbcTemplate.queryForList(sql,new Object[] {productOfferId,"%"+productOfferName+"%",pageNo-1, pageElementSize});

    } catch (Exception e) {
        System.out.println(e);
        e.printStackTrace();
        return null;
    }
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.