Wie erhalte ich die Größe eines java.sql.ResultSet?

284

Sollte dies nicht eine ziemlich einfache Operation sein? Ich sehe jedoch, dass es weder eine size()noch eine length()Methode gibt.

Jake
quelle
11
Ich würde gerne den Grund für diese Unterlassung kennen.
Slamice
Mein Verständnis der Frage war, dass Sie die Größe des ResultSet IN BYTES ermitteln möchten, nicht die Anzahl der Tupel ...
DejanLekic
Es ist sehr ärgerlich, vor Prozessdaten nicht die richtige Dimension zu haben. Wenn Sie sie jedoch in einem Array speichern müssen, können Sie eine Datenstruktur wie List verwenden und sie dann mit der toArray () -Methode in ein Array konvertieren.
AndreaTaroni86

Antworten:

270

SELECT COUNT(*) FROM ...Führen Sie stattdessen eine Abfrage durch.

ODER

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

In beiden Fällen müssen Sie nicht die gesamten Daten durchlaufen.

finnw
quelle
8
last () und getRow () sind keine statischen Methoden in der ResultSet-Klasse.
JeeBee
70
Der Kürze halber beziehe ich mich immer auf Methoden auf diese Weise, wenn ich anderen darüber schreibe, unabhängig davon, ob sie statisch sind oder nicht. Das tatsächliche Erstellen einer Instanz des Objekts und das Aufrufen der Methode ist impliziert.
Laz
51
Ich schreibe SomeClass.staticMethod () und SomeClass # instanceMethod () für weniger Verwirrung.
Jake
9
Wie ruft man den Wert ab, der bei der Ausführung von a zurückgegeben wird select count?
Naftuli Kay
16
ResultSet#last()funktioniert nicht bei allen Arten von ResultSetObjekten, Sie müssen sicherstellen, dass Sie eines verwenden, das entweder ResultSet.TYPE_SCROLL_INSENSITIVEoderResultSet.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
}
JeeBee
quelle
5
Wäre im Codeblock if (rs.last ()) nicht die richtige Methode rs.beforeFirst () anstelle von rs.first ()? Auf diese Weise überspringen Sie nicht den ersten Datensatz in Ihrer Ergebnismenge für die Verarbeitung in der while-Schleife.
Karlgrz
Vergessen Sie nicht, den Cursor außerhalb des if-Blocks auf beforeFirst zurückzusetzen?
Gobliins
Wie ResultSet-Dokumente sagen, getRow()funktioniert es für TYPE_FORWARD_ONLYResultSets und beforeFirst()wirft Fehler für diese aus. Ist diese Antwort dann nicht fehlerhaft?
CodePro_NotYet
5
Dies funktioniert nur, wenn die Anweisung mit der Option ps=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
scrollunempfindlich
19

Nun, wenn Sie einen ResultSetTyp haben ResultSet.TYPE_FORWARD_ONLY, möchten Sie ihn so lassen (und nicht zu einem ResultSet.TYPE_SCROLL_INSENSITIVEoder wechseln, ResultSet.TYPE_SCROLL_INSENSITIVEum ihn verwenden zu können .last()).

Ich schlage einen sehr schönen und effizienten Hack vor, bei dem Sie oben eine erste falsche / falsche Zeile hinzufügen, die die Anzahl der Zeilen enthält.

Beispiel

Angenommen, Ihre Anfrage lautet wie folgt

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

und Ihre Ausgabe sieht aus wie

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]

Refaktorieren Sie Ihren Code einfach wie folgt:

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);

Ihre Abfrageausgabe wird nun so etwas wie sein

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]

Also musst du einfach

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
quelle
Interessant, aber wie würden Sie dynamisch / generisch erste select-Anweisungen generieren: cast (null als Boolescher Wert) als MYBOOL, ect? Dazu benötigen Sie Metadaten der Felder und Datentypen der "select" -Anweisung (z. B. boolean, char, int, ect ...), für die möglicherweise eine zusätzliche DB-Auslösung erforderlich ist, die alle Vorteile zunichte macht.
user1697575
Dies ist nützlich, wenn Sie Zugriff auf alle Felddetails haben und die Geschwindigkeit Ihr Hauptanliegen ist (und daher bei einem schnellen bleiben müssen ResultSet.TYPE_FORWARD_ONLY)
Unai Vivi
11
int i = 0;
while(rs.next()) {
    i++;
}
Bhaskar
quelle
Ich verstehe nicht, was der Nachteil der Verwendung dieser Methode zur Berechnung der ResultSet-Größe ist. Das ist großartig ... keine Verwendung eines zusätzlichen SQL-Parameters. Bitte kommentieren Sie diese Methode.
Madeyedexter
5
Leistung ist hier das Schlüsselwort. Stellen Sie sich vor, Ihre Ergebnismenge besteht aus 100 Millionen Datensätzen. Dann wird das Problem
Pierre,
7
Ich möchte die Größe der Ergebnismenge wissen, bevor ich die Ergebnisse verarbeite, da ich vorher ein Array mit derselben Größe erstellen muss. Und wie in anderen Antworten erwähnt, funktioniert das zweimalige Scannen aller Zeilen nicht immer.
Ivo
11

Ich habe eine Ausnahme bei der Verwendung rs.last()

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

::

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

Dies liegt standardmäßig daran ResultSet.TYPE_FORWARD_ONLY, was bedeutet, dass Sie nur verwenden könnenrs.next()

Die Lösung ist:

stmt=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY); 
Dan
quelle
12
Der Wechsel von ResultSet.TYPE_FORWARD_ONLYzu führt ResultSet.TYPE_SCROLL_INSENSITIVEnormalerweise zu einem enormen Leistungsverlust.
Unai Vivi
3
Ich habe es auf meiner Tabelle getestet (10 Spalten, 187 392 Zeilen). Mein Test hat alle Elemente abgefragt und in eine Zeichenfolge geladen. Für TYPE_FORWARD_ONLY dauerte es ca. 1 Sekunde. Für TYPE_SCROLL_INSENSITIVE dauerte es ca. 7 Sekunden. Als ich eher SELECT COUNT(*) FROM default_tblvor dem benutzte SELECT COUNT(*) FROM default_tbl, dauerte es insgesamt weniger als 1,5 Sekunden. Ich habe auf eingebetteten Derby-Datenbank 10.11.1.1
Vit Bernatik
4

Die Methode zum Abrufen der Größe von ResultSet, keine Verwendung von ArrayList usw.

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

Jetzt erhalten Sie die Größe. Wenn Sie das ResultSet drucken möchten, verwenden Sie vor dem Drucken auch die folgende Codezeile:

rs.beforeFirst();  
Anptk
quelle
4

[Geschwindigkeitsüberlegung]

Viele Leute hier schlagen vor, ResultSet.last()aber dafür müssten Sie die Verbindung öffnen, da ResultSet.TYPE_SCROLL_INSENSITIVEdie für Derby eingebettete Datenbank bis zu 10-mal langsamer ist als ResultSet.TYPE_FORWARD_ONLY.

Laut meinen Mikrotests für eingebettete Derby- und H2-Datenbanken ist es wesentlich schneller, SELECT COUNT(*)vor Ihrem SELECT anzurufen .

Hier finden Sie detaillierter meinen Code und meine Benchmarks

Vit Bernatik
quelle
3

Es ist eine einfache Möglichkeit, Zeilen zu zählen.

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
quelle
4
Ja, das funktioniert. Ich denke jedoch, dass das OP Schwierigkeiten hat, die Anzahl der Zeilen zu kennen, bevor sie tatsächlich verarbeitet werden. Gründe aus dem wirklichen Leben Ich müsste dieses Problem bisher bekämpfen: 1.) Blättern in Datensatzzeilen 2.)
Anzeigen
Die Vorbelegung der Datenstrukturgröße ist ein weiterer Grund. Ich habe gesehen, dass viele Bibliotheken 10 Elementlisten zurückgeben, wenn es nur einen einzigen Wert gibt, da die Entwickler das gleiche Problem mit ResultSet hatten.
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
quelle
1

Ich habe den Laufzeitwert der ResultSet- Schnittstelle überprüft und festgestellt, dass es sich die ganze Zeit über um ein ResultSetImpl handelt . ResultSetImpl hat eine Methode namens, getUpdateCount()die den gesuchten Wert zurückgibt.

Dieses Codebeispiel sollte ausreichen:
ResultSet resultSet = executeQuery(sqlQuery);
double rowCount = ((ResultSetImpl)resultSet).getUpdateCount()

Mir ist klar, dass Downcasting im Allgemeinen ein unsicheres Verfahren ist, aber diese Methode hat mich noch nicht gescheitert.

Clausavram
quelle
2
Funktioniert nicht mit Tomcat / MySQL:java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp.DelegatingResultSet cannot be cast to com.mysql.jdbc.ResultSetImpl
Panu Haaramo
1

Heute habe ich diese Logik verwendet, warum ich nicht weiß, wie man RS zählt.

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
quelle
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
quelle
0

Ich hatte das gleiche Problem. Verwenden Sie ResultSet.first()auf diese Weise direkt nach der Ausführung gelöst:

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

Dokumentation ( Link ):

boolean first()
    throws SQLException

Bewegt den Cursor in die erste Zeile dieses ResultSetObjekts.

Kehrt zurück:

truewenn sich der Cursor in einer gültigen Zeile befindet; falsewenn die Ergebnismenge keine Zeilen enthält

Würfe:

SQLException- wenn ein Datenbankzugriffsfehler auftritt; Diese Methode wird für eine geschlossene Ergebnismenge aufgerufen oder der Ergebnismengen-Typ istTYPE_FORWARD_ONLY

SQLFeatureNotSupportedException - Wenn der JDBC-Treiber diese Methode nicht unterstützt

Schon seit:

1.2

Israel Hernández
quelle
0

Am einfachsten ist es, die Abfrage Count (*) auszuführen, resultSet.next () auszuführen, um auf die erste Zeile zu zeigen, und dann einfach resultSet.getString (1) auszuführen, um die Anzahl abzurufen. Code:

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

Geben Sie der Spalte einen Namen.

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

Verweisen Sie auf diese Spalte aus dem ResultSet-Objekt in ein int und führen Sie von dort aus Ihre Logik aus.

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
quelle