Paging dengan Oracle

97

Saya tidak begitu familiar dengan Oracle seperti yang saya inginkan. Saya memiliki sekitar 250 ribu rekaman, dan saya ingin menampilkannya 100 per halaman. Saat ini saya memiliki satu prosedur tersimpan yang mengambil semua seperempat juta catatan ke dataset menggunakan adaptor data, dan dataset, dan metode dataadapter.Fill (dataset) pada hasil dari proc yang disimpan. Jika saya memiliki "Nomor Halaman" dan "Jumlah data per halaman" sebagai nilai bilangan bulat yang dapat saya berikan sebagai parameter, cara terbaik apa untuk mendapatkan kembali bagian tersebut saja. Katakanlah, jika saya meneruskan 10 sebagai nomor halaman, dan 120 sebagai jumlah halaman, dari pernyataan pilih itu akan memberi saya 1880 hingga 1200, atau sesuatu seperti itu, matematika di kepala saya mungkin salah.

Saya melakukan ini di .NET dengan C #, saya pikir itu tidak penting, jika saya bisa melakukannya dengan benar di sisi sql, maka saya harus tenang.

Pembaruan: Saya dapat menggunakan saran Brian, dan itu bekerja dengan baik. Saya ingin mengerjakan beberapa pengoptimalan, tetapi halaman muncul dalam 4 hingga 5 detik, bukan satu menit, dan kontrol halaman saya dapat berintegrasi dengan sangat baik dengan procs baru saya yang tersimpan.

stephenbayer
sumber

Jawaban:

147

Sesuatu seperti ini seharusnya berhasil: Dari Blog 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)
Brian Schmitt
sumber
4
Ya, itu adalah kolom 'built in' yang didukung Oracle, selalu dimulai dari 1 dan bertambah untuk setiap baris. Jadi dalam potongan kode ini, jika Anda memiliki 1000 baris, tata urutan diterapkan dan kemudian setiap baris diberi rownum. Pilihan terluar menggunakan nomor baris tersebut untuk menemukan 'halaman' yang Anda cari berdasarkan ukuran halaman Anda.
Brian Schmitt
9
Ini bagus, tetapi sangat lambat pada pemilihan besar, periksa saja waktu untuk memilih 0 hingga 1000 dan 500.000 hingga 501.000 ... Saya menggunakan struktur pemilihan semacam ini sekarang saya sedang mencari solusi.
Newhouse
3
@ n3whous3 Anda dapat mencoba ini - inf.unideb.hu/~gabora/pagination/results.html
jasonk
7
Saya bertanya-tanya mengapa dua WHEREtidak dapat digabungkan AND, dan kemudian menemukan ini: orafaq.com/wiki/ROWNUM
Mengdi Gao
1
Penomoran halaman Oracle merusak hari-hariku.
Aetherus
135

Tanya Tom tentang pagination dan fungsi analitik yang sangat, sangat berguna.

Ini kutipan dari halaman itu:

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;
Chobicus
sumber
7
Ini sebenarnya adalah implementasi yang jauh lebih baik, meskipun sulit ditemukan di pos itu. Jika Anda memiliki banyak halaman besar, jawaban lain harus mencakup semua baris dari halaman sebelumnya juga. Dalam kueri yang rumit, ini berarti bahwa halaman selanjutnya berperforma lebih buruk daripada halaman sebelumnya.
tallseth
@tokopedia Anda benar. Sulit menemukannya di halaman itu. Kutipan ditambahkan.
Chobicus
Ini adalah jawaban yang benar jika Anda ingin mengubah pesanan Anda secara dinamis.
chakeda
75

Demi kelengkapan, bagi orang yang mencari solusi yang lebih modern, di Oracle 12c ada beberapa fitur baru termasuk paging yang lebih baik dan penanganan teratas.

Paging

Halamannya terlihat seperti ini:

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

Rekaman N Teratas

Mendapatkan rekor teratas terlihat seperti ini:

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

Perhatikan bagaimana kedua contoh kueri di atas memiliki ORDER BYklausa. Perintah baru menghormati ini dan dijalankan pada data yang diurutkan.

Saya tidak dapat menemukan halaman referensi Oracle yang bagus untuk FETCHatau OFFSETtetapi halaman ini memiliki gambaran umum yang bagus tentang fitur-fitur baru ini.

Performa

Seperti yang ditunjukkan @wweicker pada komentar di bawah, kinerja adalah masalah dengan sintaks baru di 12c. Saya tidak memiliki salinan 18c untuk menguji apakah Oracle telah memperbaikinya.

Yang cukup menarik, hasil aktual saya dikembalikan sedikit lebih cepat saat pertama kali saya menjalankan kueri di tabel saya (113 juta + baris) untuk metode baru:

  • Metode baru: 0,013 detik.
  • Metode lama: 0,107 detik.

Namun, seperti yang disebutkan @wweicker, paket penjelasan terlihat jauh lebih buruk untuk metode baru:

  • Biaya metode baru: 300.110
  • Biaya metode lama: 30

Sintaks baru menyebabkan pemindaian penuh indeks di kolom saya, yang merupakan seluruh biaya. Kemungkinannya adalah, segalanya menjadi lebih buruk ketika membatasi pada data yang tidak diindeks.

Mari kita lihat saat menyertakan satu kolom yang tidak terindeks pada kumpulan data sebelumnya:

  • Waktu / biaya metode baru: 189,55 detik / 998.908
  • Waktu / biaya metode lama: 1,973 detik / 256

Ringkasan: gunakan dengan hati-hati sampai Oracle meningkatkan penanganan ini. Jika Anda memiliki indeks untuk dikerjakan, mungkin Anda bisa lolos dengan menggunakan metode baru.

Mudah-mudahan saya akan memiliki salinan 18c untuk segera dimainkan dan dapat memperbarui

JoelC
sumber
Ini adalah jawaban yang bagus untuk pengguna 12c
Lalji Gajera
1
Sintaks lebih bersih, tetapi kinerjanya lebih buruk ( dba-presents.com/index.php/databases/oracle/… )
anyaman
Senang mengetahuinya, terima kasih @wweicker. Semoga kinerja segera diperbaiki oleh Oracle; meskipun, mengetahui Oracle, ini mungkin harapan yang jauh!
JoelC
Sintaksnya baru dan diubah menjadi panggilan ROW_NUMBER / RANK biasa. Terkait Bagaimana cara membatasi jumlah baris yang dikembalikan oleh kueri Oracle setelah memesan?
Lukasz Szozda
@JoelC, apakah ada perubahan pada opini Anda?
Ryan
11

Hanya ingin merangkum jawaban dan komentar. Ada beberapa cara melakukan penomoran halaman.

Sebelum oracle 12c tidak ada fungsi OFFSET / FETCH, jadi lihatlah whitepaper seperti yang disarankan @jasonk. Ini artikel terlengkap yang saya temukan tentang berbagai metode dengan penjelasan rinci tentang kelebihan dan kekurangan. Ini akan memakan banyak waktu untuk menyalin dan menempelkannya di sini, jadi saya tidak akan melakukannya.

Ada juga artikel bagus dari pencipta jooq menjelaskan beberapa peringatan umum dengan oracle dan pagination database lainnya. posting blog jooq

Kabar baik, sejak oracle 12c kami memiliki fungsionalitas OFFSET / FETCH baru. Fitur baru OracleMagazine 12c . Silakan merujuk ke "Top-N Queries and Pagination"

Anda dapat memeriksa versi oracle Anda dengan mengeluarkan pernyataan berikut

SELECT * FROM V$VERSION
Vadim Kirilchuk
sumber
7

Coba yang berikut ini:

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;

melalui [tecnicume]

Furetto
sumber
0

Dalam proyek saya, saya menggunakan Oracle 12c dan java . Kode halamannya terlihat seperti ini:

 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;
    }
Ferdous Wahid
sumber