Setara dengan LIMIT untuk DB2

93

Bagaimana Anda melakukannya LIMITdi DB2 untuk iSeries?

Saya memiliki tabel dengan lebih dari 50.000 catatan dan saya ingin mengembalikan catatan 0 hingga 10.000, dan catatan 10.000 hingga 20.000.

Saya tahu di SQL Anda menulis LIMIT 0,10000di akhir kueri untuk 0 hingga 10.000 dan LIMIT 10000,10000di akhir kueri untuk 10.000 hingga 20.000

Jadi, bagaimana ini dilakukan di DB2? Apa kode dan sintaksnya? (contoh kueri lengkap dihargai)

elcool.dll
sumber
ROW_NUMBER () hanya diterapkan di iSeries DB2 V5R4. Untuk versi sebelumnya coba gunakan RRN () yang serupa.
Paul Morgan
RRN () sama sekali berbeda dari row_number ().
Brandon Peterson
tidak bekerja untuk saya. Kesalahan Sytanx.
elcool
1
Coba RRN (nama file) yang akan memberikan nomor catatan relatif fisik baris. RRN tidak akan berurutan dan dapat melewati angka jika baris telah dihapus. RRN juga tidak akan diurutkan berdasarkan kunci tetapi akan diurutkan berdasarkan penambahan jika tidak ada penghapusan yang terjadi. Bagaimanapun RRN akan unik untuk satu baris dan dapat digunakan untuk memilih subset dari tabel.
Paul Morgan
1
DB2 memberikan dukungan kata kunci batas dari DB2 9.7.2 menurut programmingzen.com/2010/06/02/…
lakshman

Jawaban:

141

Menggunakan FETCH FIRST [n] ROWS ONLY:

http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.perf/db2z_fetchfirstnrows.htm

SELECT LASTNAME, FIRSTNAME, EMPNO, SALARY
  FROM EMP
  ORDER BY SALARY DESC
  FETCH FIRST 20 ROWS ONLY;

Untuk mendapatkan rentang, Anda harus menggunakan ROW_NUMBER()(sejak v5r4) dan menggunakannya dalam WHEREklausa: (dicuri dari sini: http://www.justskins.com/forums/db2-select-how-to-123209.html )

SELECT code, name, address
FROM ( 
  SELECT row_number() OVER ( ORDER BY code ) AS rid, code, name, address
  FROM contacts
  WHERE name LIKE '%Bob%' 
  ) AS t
WHERE t.rid BETWEEN 20 AND 25;
Joe
sumber
ya, saya menemukan ini juga, hehe. Saya mengedit pertanyaan pada saat yang sama untuk menunjukkan bahwa saya juga menginginkan baris tengah.
elcool
2
Anda harus melakukan sesuatu seperti ini dengan ROW_NUMBER: justskins.com/forums/db2-select-how-to-123209.html
Joe
ROW_NUMBERbukan kata kunci yang valid. Tapi terima kasih untuk tautannya, itu memberi saya ide dan berhasil.
elcool
13

Mengembangkan metode ini:

Anda MEMBUTUHKAN meja yang memiliki nilai unik yang dapat dipesan.

Jika Anda menginginkan baris 10.000 hingga 25.000 dan Tabel Anda memiliki 40.000 baris, pertama-tama Anda perlu mendapatkan titik awal dan total baris:

int start = 40000 - 10000;

int total = 25000 - 10000;

Dan kemudian berikan ini dengan kode ke kueri:

SELECT * FROM 
(SELECT * FROM schema.mytable 
ORDER BY userId DESC fetch first {start} rows only ) AS mini 
ORDER BY mini.userId ASC fetch first {total} rows only
elcool.dll
sumber
Perhatikan bahwa baris ke-10.000 dikeluarkan dari kumpulan hasil, baris pertama adalah baris ke-1000.
kebiruan pada
1
Solusi yang menarik. Saya akan menggunakannya untuk kompatibilitas dengan database uji H2 ... Tapi, sayangnya, ini bekerja ~ 30 kali lebih lambat dari pendekatan SELECT row_number () OVER (ORDER BY code).
manuna
9

Dukungan untuk OFFSET dan LIMIT baru-baru ini ditambahkan ke DB2 untuk i 7.1 dan 7.2. Anda memerlukan tingkat grup PTF DB berikut untuk mendapatkan dukungan ini:

  • SF99702 level 9 untuk IBM i 7.2
  • SF99701 level 38 untuk IBM i 7.1

Lihat di sini untuk informasi lebih lanjut: dokumentasi OFFSET dan LIMIT , DB2 untuk i Enhancement Wiki

Kevin Adler
sumber
7

Inilah solusi yang saya dapatkan:

select FIELD from TABLE where FIELD > LASTVAL order by FIELD fetch first N rows only;

Dengan menginisialisasi LASTVAL ke 0 (atau '' untuk bidang teks), lalu menyetelnya ke nilai terakhir di kumpulan rekaman terbaru, ini akan menelusuri tabel dalam potongan N catatan.

Tom Barron
sumber
(Awalnya saya mengira Anda menetapkan nilai dalam tabel, yang akan sangat bermasalah pada sistem konkuren) Ya, ini seharusnya berfungsi jika Anda melakukan pembacaan sekuensial melalui tabel, meskipun Anda memerlukan semacam kolom tie-breaker dalam kasus di mana Nlebih kecil dari jumlah nilai yang identik di kolom (meskipun ini benar saat menggunakan ROW_NUMBER()juga). Nilai awal juga harus dipilih dengan hati-hati - 0jelas akan bermasalah jika kolom berisi nilai negatif . Perawatan akan dibutuhkan dengan nulls. Tidak akan berfungsi jika halaman dilewati.
Clockwork-Muse
Terima kasih atas komentarnya. Saya pikir ada asumsi implisit bahwa bidang yang kita gunakan untuk mengontrol kueri itu unik dan meningkat secara monoton. Saya setuju bahwa jika asumsi tersebut tidak berlaku, ini tidak akan berhasil untuk mengunjungi semua catatan di tabel. Dan, tentu saja, Anda benar bahwa Anda harus memulai dengan LASTVAL yang masuk akal. Secara umum, saya pikir Anda ingin memulai dengan apa pun yang dikembalikan oleh "pilih MINIMUM (FIELD) dari TABLE". Jika bidang diindeks, sebagian besar mesin db akan bekerja lebih baik daripada membaca seluruh tabel secara berurutan.
Tom Barron
2

Solusi @ elcool adalah ide yang cerdas, tetapi Anda perlu mengetahui jumlah baris (yang bahkan dapat berubah saat Anda menjalankan kueri!). Jadi saya mengusulkan versi modifikasi, yang sayangnya membutuhkan 3 subkueri, bukan 2:

select * from (
    select * from (
        select * from MYLIB.MYTABLE
        order by MYID asc 
        fetch first {last} rows only 
        ) I 
    order by MYID desc
    fetch first {length} rows only
    ) II
order by MYID asc

dimana {last}harus diganti dengan nomor baris dari record terakhir yang saya butuhkan dan {length}harus diganti dengan jumlah baris yang saya butuhkan, dihitung seperti last row - first row + 1.

Misalnya jika saya ingin baris dari 10 hingga 25 (total 16 baris), {last}akan menjadi 25 dan {length}akan menjadi 25-10 + 1 = 16.

kebiruan
sumber
Saya membenci mereka yang tidak menyukai ketika orang lain meluangkan waktu untuk menjawab pertanyaan mereka.
jp2code
1

Anda juga harus mempertimbangkan klausa OPTIMALKAN UNTUK n ROWS. Detail lebih lanjut tentang semua ini dalam dokumentasi DB2 LUW di Panduan untuk membatasi topik pernyataan SELECT :

  • Klausa OPTIMIZE FOR mendeklarasikan maksud untuk mengambil hanya subset dari hasil atau memberikan prioritas untuk mengambil hanya beberapa baris pertama. Pengoptimal kemudian dapat memilih rencana akses yang meminimalkan waktu respons untuk mengambil beberapa baris pertama.
David Sky
sumber
1

Coba ini

SELECT * FROM
    (
        SELECT T.*, ROW_NUMBER() OVER() R FROM TABLE T
    )
    WHERE R BETWEEN 10000 AND 20000
Lucio Menci
sumber
0

Ada 2 solusi untuk membuat nomor halaman secara efisien pada tabel DB2:

1 - teknik menggunakan fungsi row_number () dan klausa OVER yang telah disajikan pada posting lain ("SELECT row_number () OVER (ORDER BY ...)"). Pada beberapa tabel besar, saya terkadang melihat penurunan kinerja.

2 - teknik menggunakan kursor yang dapat digulir. Implementasinya tergantung dari bahasa yang digunakan. Teknik itu tampaknya lebih kuat di tabel besar.

Saya mempresentasikan 2 teknik yang diterapkan di PHP selama seminar tahun depan. Slide tersedia di tautan ini: http://gregphplab.com/serendipity/uploads/slides/DB2_PHP_Best_practices.pdf

Maaf, dokumen ini hanya dalam bahasa Prancis.

gregphplab.dll
sumber
0

Ada opsi yang tersedia ini: -

DB2 has several strategies to cope with this problem.
You can use the "scrollable cursor" in feature.
In this case you can open a cursor and, instead of re-issuing a query you can FETCH forward and backward.
This works great if your application can hold state since it doesn't require DB2 to rerun the query every time.
You can use the ROW_NUMBER() OLAP function to number rows and then return the subset you want.
This is ANSI SQL 
You can use the ROWNUM pseudo columns which does the same as ROW_NUMBER() but is suitable if you have Oracle skills.
You can use LIMIT and OFFSET if you are more leaning to a mySQL or PostgreSQL dialect.  
Hector
sumber