Bagaimana cara mendapatkan ukuran java.sql.ResultSet?

284

Bukankah ini operasi yang sangat mudah? Namun, saya melihat tidak ada metode size()maupun length()metode.

Jake
sumber
11
Saya ingin tahu alasan penghilangan itu.
Slamice
Pemahaman saya tentang pertanyaan itu adalah bahwa Anda ingin menemukan ukuran ResultSet DI BYTES, bukan jumlah tuple ...
DejanLekic
Sangat menjengkelkan karena tidak memiliki dimensi yang tepat sebelum memproses data, tetapi jika Anda harus menyimpannya dalam larik, Anda dapat mempertimbangkan menggunakan struktur data seperti Daftar dan kemudian mengonversinya menjadi larik dengan metode toArray ().
AndreaTaroni86

Jawaban:

270

Lakukan SELECT COUNT(*) FROM ...kueri sebagai gantinya.

ATAU

int size =0;
if (rs != null) 
{
  rs.last();    // moves cursor to the last row
  size = rs.getRow(); // get row id 
}

Dalam salah satu kasus, Anda tidak perlu mengulang seluruh data.

menemukan
sumber
8
last () dan getRow () bukan metode statis di kelas ResultSet.
JeeBee
70
Demi singkatnya, saya selalu referensi metode dalam mode ini ketika menulis tentang mereka kepada orang lain, terlepas dari apakah mereka statis atau tidak. Sebenarnya membuat instance objek dan memanggil metode tersirat.
laz
51
Saya menulis SomeClass.staticMethod () dan SomeClass # instanceMethod () untuk mengurangi kebingungan.
Jake
9
Bagaimana cara mengambil nilai yang dikembalikan saat mengeksekusi select count?
Naftuli Kay
16
ResultSet#last()tidak bekerja pada semua jenis ResultSetobjek, Anda perlu memastikan bahwa Anda menggunakan salah satu dari keduanya ResultSet.TYPE_SCROLL_INSENSITIVEatauResultSet.TYPE_SCROLL_SENSITIVE
Marius Ion
91
ResultSet rs = ps.executeQuery();
int rowcount = 0;
if (rs.last()) {
  rowcount = rs.getRow();
  rs.beforeFirst(); // not rs.first() because the rs.next() below will move on, missing the first element
}
while (rs.next()) {
  // do your standard per row stuff
}
Astaga
sumber
5
Di dalam blok kode if (rs.last ()), bukankah metode yang benar adalah rs.beforeFirst () alih-alih rs.first ()? Dengan cara ini, Anda tidak melewatkan rekaman pertama di set hasil Anda untuk diproses di loop sementara.
karlgrz
jangan lupa untuk mengatur kursor kembali ke beforeFirst di luar blok if?
Goblin
Seperti yang dikatakan dokumen ResultSet , getRow()berfungsi untuk TYPE_FORWARD_ONLYResultSet, dan beforeFirst()melempar kesalahan untuk itu. Bukankah jawaban ini salah?
CodePro_NotYet
5
Ini hanya berfungsi ketika pernyataan dibuat dengan opsi gulir tidak sensitif:ps=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
BullyWiiPlaza
19

Nah, jika Anda memiliki ResultSettipeResultSet.TYPE_FORWARD_ONLY Anda ingin tetap seperti itu (dan tidak beralih ke ResultSet.TYPE_SCROLL_INSENSITIVEatau ResultSet.TYPE_SCROLL_INSENSITIVEagar dapat digunakan .last()).

Saya sarankan hack yang sangat bagus dan efisien, di mana Anda menambahkan baris palsu / palsu pertama di bagian atas yang berisi jumlah baris.

Contoh

Katakanlah permintaan Anda adalah sebagai berikut

select MYBOOL,MYINT,MYCHAR,MYSMALLINT,MYVARCHAR
from MYTABLE
where ...blahblah...

dan output Anda terlihat seperti

true    65537 "Hey" -32768 "The quick brown fox"
false  123456 "Sup"    300 "The lazy dog"
false -123123 "Yo"       0 "Go ahead and jump"
false       3 "EVH"    456 "Might as well jump"
...
[1000 total rows]

Cukup refactor kode Anda ke sesuatu seperti ini:

Statement s=myConnection.createStatement(ResultSet.TYPE_FORWARD_ONLY,
                                         ResultSet.CONCUR_READ_ONLY);
String from_where="FROM myTable WHERE ...blahblah... ";
//h4x
ResultSet rs=s.executeQuery("select count(*)as RECORDCOUNT,"
                           +       "cast(null as boolean)as MYBOOL,"
                           +       "cast(null as int)as MYINT,"
                           +       "cast(null as char(1))as MYCHAR,"
                           +       "cast(null as smallint)as MYSMALLINT,"
                           +       "cast(null as varchar(1))as MYVARCHAR "
                           +from_where
                           +"UNION ALL "//the "ALL" part prevents internal re-sorting to prevent duplicates (and we do not want that)
                           +"select cast(null as int)as RECORDCOUNT,"
                           +       "MYBOOL,MYINT,MYCHAR,MYSMALLINT,MYVARCHAR "
                           +from_where);

Output kueri Anda sekarang akan menjadi seperti

1000 null     null null    null null
null true    65537 "Hey" -32768 "The quick brown fox"
null false  123456 "Sup"    300 "The lazy dog"
null false -123123 "Yo"       0 "Go ahead and jump"
null false       3 "EVH"    456 "Might as well jump"
...
[1001 total rows]

Jadi kamu harus

if(rs.next())
    System.out.println("Recordcount: "+rs.getInt("RECORDCOUNT"));//hack: first record contains the record count
while(rs.next())
    //do your stuff
Unai Vivi
sumber
Menarik, tetapi bagaimana Anda secara dinamis / umum menghasilkan pernyataan pilihan pertama: cast (null as boolean) sebagai MYBOOL, dll? Untuk itu Anda akan membutuhkan metadata dari kolom pernyataan "pilih" dan tipe data, seperti boolean, char, int, dll ...) yang mungkin memerlukan perjalanan DB ekstra yang akan meniadakan semua manfaat.
user1697575
Ini berguna ketika Anda memiliki akses ke semua detail bidang dan kecepatan adalah perhatian utama Anda (dan karena itu harus tetap berpegang teguh ResultSet.TYPE_FORWARD_ONLY)
Unai Vivi
11
int i = 0;
while(rs.next()) {
    i++;
}
bhaskar
sumber
Saya tidak mengerti apa kelemahan menggunakan metode ini untuk menghitung ukuran ResultSet. Ini hebat ... tidak menggunakan parameter SQL tambahan. Berikan komentar tentang metode ini.
Madeyedexter
5
Kinerja adalah kata kunci di sini. Bayangkan resultset Anda adalah 100 juta catatan maka Anda akan melihat masalahnya
Pierre
7
Saya ingin tahu ukuran hasil set SEBELUM memproses hasil karena saya perlu membuat array dengan ukuran yang sama sebelumnya. Dan, seperti disebutkan dalam jawaban lain, pemindaian semua baris dua kali tidak akan selalu berhasil.
Ivo
11

Saya mendapat pengecualian saat menggunakan rs.last()

if(rs.last()){
    rowCount = rs.getRow(); 
    rs.beforeFirst();
}

:

java.sql.SQLException: Invalid operation for forward only resultset

itu karena secara default itu ResultSet.TYPE_FORWARD_ONLY, yang berarti Anda hanya dapat menggunakanrs.next()

solusinya adalah:

stmt=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY); 
Dan
sumber
12
Beralih dari ResultSet.TYPE_FORWARD_ONLYke ResultSet.TYPE_SCROLL_INSENSITIVEbiasanya menimbulkan dalam besar hukuman kinerja.
Unai Vivi
3
Saya memang mengujinya di meja saya (10 kolom, 187 392 baris). Pengujian saya melakukan kueri dan memuat semua elemen ke string. Untuk TYPE_FORWARD_ONLY butuh sekitar 1 detik. Untuk TYPE_SCROLL_INSENSITIVE butuh sekitar 7 detik. Ketika saya menggunakan agak SELECT COUNT(*) FROM default_tblsebelum SELECT COUNT(*) FROM default_tblbutuh waktu kurang dari 1,5 detik. Saya menguji pada database derby tertanam 10.11.1.1
Vit Bernatik
4

Cara mendapatkan ukuran ResultSet, Tidak perlu menggunakan ArrayList dll

int size =0;  
if (rs != null)   
{  
rs.beforeFirst();  
 rs.last();  
size = rs.getRow();
}

Sekarang Anda akan mendapatkan ukuran, dan jika Anda ingin mencetak ResultSet, sebelum mencetak menggunakan baris kode berikut juga,

rs.beforeFirst();  
Anptk
sumber
4

[Pertimbangan kecepatan]

Banyak ppl di sini menyarankan ResultSet.last()tetapi untuk itu Anda perlu membuka koneksi karena ResultSet.TYPE_SCROLL_INSENSITIVEyang untuk database tertanam Derby hingga 10 kali lebih lambat daripada ResultSet.TYPE_FORWARD_ONLY.

Menurut tes mikro saya untuk database Derby dan H2 yang tertanam, secara signifikan lebih cepat untuk menelepon SELECT COUNT(*)sebelum SELECT Anda.

Ini adalah lebih detail kode saya dan tolok ukur saya

Vit Bernatik
sumber
3

Ini adalah cara sederhana untuk melakukan rows-count.

ResultSet rs = job.getSearchedResult(stmt);
int rsCount = 0;

//but notice that you'll only get correct ResultSet size after end of the while loop
while(rs.next())
{
    //do your other per row stuff 
    rsCount = rsCount + 1;
}//end while
CounterSpell
sumber
4
Ya, itu berhasil. Tapi saya pikir OP berjuang dengan mengetahui jumlah baris sebelum benar-benar memprosesnya. Alasan kehidupan nyata saya harus berjuang dengan masalah ini sejauh ini: 1.) paging baris record 2.) menunjukkan baris diproses dalam tugas jangka panjang untuk keperluan pemantauan kemajuan ...
ppeterka
Preallocating ukuran struktur data adalah alasan lain. Saya telah melihat banyak lib mengembalikan 10 Daftar elemen ketika hanya ada satu nilai karena dev memiliki masalah yang sama dengan ResultSet.
Joseph Lust
2
String sql = "select count(*) from message";
ps =  cn.prepareStatement(sql);

rs = ps.executeQuery();
int rowCount = 0;
while(rs.next()) {
    rowCount = Integer.parseInt(rs.getString("count(*)"));
    System.out.println(Integer.parseInt(rs.getString("count(*)")));
}
System.out.println("Count : " + rowCount);
Peter.Chu
sumber
1

Saya memeriksa nilai runtime dari antarmuka ResultSet dan menemukan itu adalah ResultSetImpl cukup banyak sepanjang waktu. ResultSetImpl memiliki metode yang disebutgetUpdateCount() yang mengembalikan nilai yang Anda cari.

Contoh kode ini harus cukup:
ResultSet resultSet = executeQuery(sqlQuery);
double rowCount = ((ResultSetImpl)resultSet).getUpdateCount()

Saya menyadari bahwa downcasting umumnya merupakan prosedur yang tidak aman tetapi metode ini belum mengecewakan saya.

klausavram
sumber
2
Tidak bekerja dengan Tomcat / MySQL:java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp.DelegatingResultSet cannot be cast to com.mysql.jdbc.ResultSetImpl
Panu Haaramo
1

Hari ini, saya menggunakan logika ini mengapa saya tidak tahu menghitung RS.

int chkSize = 0;
if (rs.next()) {
    do {  ..... blah blah
        enter code here for each rs.
        chkSize++;
    } while (rs.next());
} else {
    enter code here for rs size = 0 
}
// good luck to u.
Parksangdonews
sumber
0
theStatement=theConnection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

ResultSet theResult=theStatement.executeQuery(query); 

//Get the size of the data returned
theResult.last();     
int size = theResult.getRow() * theResult.getMetaData().getColumnCount();       
theResult.beforeFirst();
Ben
sumber
0

Saya mengalami masalah yang sama. Menggunakan ResultSet.first()cara ini tepat setelah eksekusi menyelesaikannya:

if(rs.first()){
    // Do your job
} else {
    // No rows take some actions
}

Dokumentasi ( tautan ):

boolean first()
    throws SQLException

Memindahkan kursor ke baris pertama di ResultSetobjek ini .

Pengembalian:

truejika kursor berada pada baris yang benar; falsejika tidak ada baris di set hasil

Melempar:

SQLException- jika terjadi kesalahan akses database; metode ini dipanggil pada himpunan hasil tertutup atau jenis himpunan hasil adalahTYPE_FORWARD_ONLY

SQLFeatureNotSupportedException - jika driver JDBC tidak mendukung metode ini

Sejak:

1.2

Israel Hernández
sumber
0

Pendekatan termudah, Jalankan Hitung Count (*), lakukan resultSet.next () untuk menunjuk ke baris pertama dan kemudian lakukan resultSet.getString (1) untuk mendapatkan hitungan. Kode:

ResultSet rs = statement.executeQuery("Select Count(*) from your_db");
if(rs.next()) {
   int count = rs.getString(1).toInt()
}
pengguna7120462
sumber
-1

Beri nama pada kolom ..

String query = "SELECT COUNT(*) as count FROM

Referensi kolom itu dari objek ResultSet ke int dan lakukan logika Anda dari sana ..

PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, item.getProductId());
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
    int count = resultSet.getInt("count");
    if (count >= 1) {
        System.out.println("Product ID already exists.");
    } else {
        System.out.println("New Product ID.");
    }
}
ReMaX
sumber