Wie erhalte ich die Zeilenanzahl mit ResultSet in Java?

75

Ich versuche, eine einfache Methode zu erstellen, die ein ResultSet als Parameter empfängt und ein int zurückgibt, das die Zeilenanzahl des ResultSet enthält. Ist dies eine gültige Methode oder nicht so sehr?

int size = 0;
    try {
        while(rs.next()){
            size++;
        }
    }
    catch(Exception ex) {
        System.out.println("------------------Tablerize.getRowCount-----------------");
        System.out.println("Cannot get resultSet row count: " + ex);
        System.out.println("--------------------------------------------------------");
    }

Ich habe es versucht:

int size = 0;
try {
    resultSet.last();
    size = resultSet.getRow();
    resultSet.beforeFirst();
}
catch(Exception ex) {
    return 0;
}
return size;

Aber ich habe einen Fehler bekommen com.microsoft.sqlserver.jdbc.SQLServerException: The requested operation is not supported on forward only result sets.

Vielen Dank im Voraus für die Hinweise!

Arpit Porwal
quelle
Können Sie die SQL-Anweisung nicht in ändern SELECT COUNT(*) ...?
Moyshe
2
Warum führen Sie einfach keine "Auswahlzählung (*)" durch, bevor Sie die Abfrage aufrufen, die die Ergebnismenge ausfüllt? ForwardOnly RS hängt jedoch davon ab, wie Sie die Verbindung öffnen (es müssen einige Parameter übergeben werden). Weitere Informationen finden Sie in der Dokumentation Ihres JDBC-Treibers.
BigMike
1
(OT; vielleicht möchten Sie eine tatsächliche Protokollierungsbibliothek verwenden, die sich um Dinge wie die Ausgabe des Methodennamens usw. kümmert.)
Dave Newton

Antworten:

66

Wenn Sie Zugriff auf die vorbereitete Anweisung haben, die zu dieser Ergebnismenge führt, können Sie verwenden

connection.prepareStatement(sql, 
  ResultSet.TYPE_SCROLL_INSENSITIVE, 
  ResultSet.CONCUR_READ_ONLY);

Dadurch wird Ihre Anweisung so vorbereitet, dass Sie den Cursor zurückspulen können. Dies ist auch im ResultSet Javadoc dokumentiert

Im Allgemeinen kann das Weiterleiten und Zurückspulen von Cursorn für große Ergebnismengen jedoch recht ineffizient sein. Eine weitere Option in SQL Server besteht darin, die Gesamtzahl der Zeilen direkt in Ihrer SQL-Anweisung zu berechnen:

SELECT my_table.*, count(*) over () total_rows
FROM my_table
WHERE ...
Lukas Eder
quelle
Ich habe Zugriff auf die vorbereitete Anweisung, bin aber in SQL in keiner Weise wirklich kompetent. Die aktuell vorbereitete Anweisung sieht folgendermaßen aus: "Wählen Sie m. * Aus [Nachrichten] m INNER JOIN RequestMessageIndex i auf m.messageGUID = i.MessageGuid, wobei i.requestfield = 'DR' und i.indexValue wie '% TAGER00%'" Muss ich der SQL-Anweisung nur Code hinzufügen oder eine andere Anweisung erstellen? Entschuldigung, ich bin wirklich dumm, wenn es um SQL geht ...
1
@ DeanGrobler: Es ist nichts Falsches daran, diese Dinge zu fragen! Beide Optionen, die ich erwähnt habe, können gültig sein. In jedem Fall kann das Zählen der Anzahl der Zeilen die Leistung bei beiden Techniken beeinträchtigen. Ich persönlich bevorzuge das zweite count(*) over (partition by 1)Feld mit einem berechneten Feld, aber nur, wenn Sie diese Anzahl für Dinge wie Paging in einer GUI verwenden können. Wenn es nur um die Protokollierung geht, bin ich mir nicht sicher, ob es im Allgemeinen eine gute Idee ist, die Zeilenanzahl zu ermitteln ...
Lukas Eder,
Dies ist auf jeden Fall erforderlich, um die Zeilenanzahl der Ergebnismenge zu erhalten. Die Variable wird verwendet, um eine dynamische Tabelle um die zurückgegebenen Daten für die GUI zu erstellen. Deff muss dies also tun. Auf der positiven Seite stammen die zurückgegebenen Daten aus einer Suchfunktion. Die Ergebnismenge würde also nur maximal 15 Zeilen betragen.
1
@ JerylCook: Es gibt eine bereits vorhandene ResultSetund sie möchten die Zeilen in dieser Ergebnismenge zählen. Was vermisse ich? "empfängt ein ResultSet als Parameter und gibt ein int zurück, das die Zeilenanzahl des ResultSet enthält" . Meine Antwort zeigte ihren Fehler (nicht spezifizierend ResultSet.TYPE_SCROLL_INSENSITIVE) und zeigte eine Alternative. Also, was genau kritisieren Sie? Beachten Sie, dass es andere Antworten gibt, die Fragen vorschlagen SELECT count(*)...
Lukas Eder
1
@ JerylCook: Prost :-)
Lukas Eder
37

Ihre SQL-Anweisung, die Code erstellt, kann wie folgt aussehen

statement = connection.createStatement();

So lösen Sie die Ausnahmeänderung "com.microsoft.sqlserver.jdbc.SQLServerException: Die angeforderte Operation wird nicht nur für Ergebnismengen mit Weiterleitung unterstützt" über dem Code mit

statement = connection.createStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE, 
    ResultSet.CONCUR_READ_ONLY);

Nach obiger Änderung können Sie verwenden

int size = 0;
try {
    resultSet.last();
    size = resultSet.getRow();
    resultSet.beforeFirst();
}
catch(Exception ex) {
    return 0;
}
return size;

um die Anzahl der Zeilen zu erhalten

Fathah Rehman P.
quelle
29
Statement s = cd.createStatement();
ResultSet r = s.executeQuery("SELECT COUNT(*) AS rowcount FROM FieldMaster");
r.next();
int count = r.getInt("rowcount");
r.close();
System.out.println("MyTable has " + count + " row(s).");

Manchmal unterstützt JDBC die folgende Methode nicht. Fehler wie "TYPE_FORWARD_ONLY" verwenden diese Lösung

Sqlite wird in JDBC nicht unterstützt.

resultSet.last();
size = resultSet.getRow();
resultSet.beforeFirst();

Verwenden Sie zu diesem Zeitpunkt diese Lösung.

Vielen Dank..

Java Man
quelle
Danke, das ist die richtige Erklärung und die richtige Lösung.
Sambuu
7

Ich habe gerade eine Getter-Methode gemacht.

public int getNumberRows(){
    try{
       statement = connection.creatStatement();
       resultset = statement.executeQuery("your query here");
       if(resultset.last()){
          return resultset.getRow();
       } else {
           return 0; //just cus I like to always do some kinda else statement.
       }
    } catch (Exception e){
       System.out.println("Error getting row count");
       e.printStackTrace();
    }
    return 0;
}
The01Guy
quelle
7
Sie führen also eine ganze Abfrage aus, um die Anzahl zu ermitteln? Bearbeiten Sie die SQL-Abfrage besser dynamisch, damit sie nur den COUNT
zurückgibt
7

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

Suganthan Madhavan Pillai
quelle
3

Wenn Sie eine Tabelle haben und die ID als primäres und automatisches Inkrement speichern, funktioniert dies

Beispielcode zum Abrufen der gesamten Zeilenanzahl http://www.java2s.com/Tutorial/Java/0340__Database/GettheNumberofRowsinaDatabaseTable.htm

Unten ist Code

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;

public class Main {
  public static void main(String[] args) throws Exception {
    Connection conn = getConnection();
    Statement st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
        ResultSet.CONCUR_UPDATABLE);

    st.executeUpdate("create table survey (id int,name varchar(30));");
    st.executeUpdate("insert into survey (id,name ) values (1,'nameValue')");
    st.executeUpdate("insert into survey (id,name ) values (2,null)");
    st.executeUpdate("insert into survey (id,name ) values (3,'Tom')");
    st = conn.createStatement();
    ResultSet rs = st.executeQuery("SELECT * FROM survey");

    rs = st.executeQuery("SELECT COUNT(*) FROM survey");
    // get the number of rows from the result set
    rs.next();
    int rowCount = rs.getInt(1);
    System.out.println(rowCount);

    rs.close();
    st.close();
    conn.close();

  }

  private static Connection getConnection() throws Exception {
    Class.forName("org.hsqldb.jdbcDriver");
    String url = "jdbc:hsqldb:mem:data/tutorial";

    return DriverManager.getConnection(url, "sa", "");
  }
}
anand
quelle
2

Ihre Funktion gibt die Größe eines ResultSet zurück, aber der Cursor wird nach dem letzten Datensatz gesetzt. Ohne Zurückspulen durch Aufrufen von beforeFirst (), first () oder previous () können Sie seine Zeilen nicht lesen und zurückspulen Methoden funktionieren nicht nur mit ResultSet vorwärts (Sie erhalten dieselbe Ausnahme wie in Ihrem zweiten Codefragment).

socha23
quelle
2

Die meisten Treiber unterstützen nur die Ergebnismenge vorwärts - daher werden Methoden wie last, beforeFirst usw. nicht unterstützt.

Der erste Ansatz ist geeignet, wenn Sie die Daten auch in derselben Schleife abrufen. Andernfalls wurde das resultSet bereits iteriert und kann nicht erneut verwendet werden.

In den meisten Fällen besteht die Anforderung darin, die Anzahl der Zeilen zu ermitteln, die eine Abfrage zurückgeben würde, ohne die Zeilen abzurufen. Das Durchlaufen der Ergebnismenge zum Ermitteln der Zeilenanzahl entspricht fast dem Verarbeiten der Daten. Es ist besser, stattdessen eine andere Zählabfrage (*) durchzuführen.

gkamal
quelle
Auf einigen Datenbanksystemen kann es sehr langsam sein, eine Select Count (*) - Abfrage durchzuführen. Daher sollte der TS möglicherweise einen Weg finden, um die Notwendigkeit zu beseitigen, die Größe der Ergebnismenge überhaupt zu kennen.
Mark Rotteveel
Ich bin mir ziemlich sicher, dass der SQL Server JDBC-Treiber zurückspulbare Ergebnismengen unterstützt. Es geht darum, die vorbereitete Anweisung korrekt zu initialisieren ... Und ich stimme @MarkRotteveel zu, dass das Ausgeben von zwei Abfragen, nur um eine Zeilenanzahl zu erhalten, fatal sein kann. Wenn es wirklich benötigt wird, können Sie jederzeit eine count(*) over (partition by 1)Fensterfunktion hinzufügen , auch wenn dies auch für große Ergebnismengen langsam sein kann ...
Lukas Eder
2

Andere haben bereits geantwortet, wie Sie Ihr Problem lösen können, daher werde ich nicht wiederholen, was bereits gesagt wurde, aber ich werde Folgendes sagen: Sie sollten wahrscheinlich einen Weg finden, um Ihre Probleme zu lösen, ohne die Anzahl der Ergebnismengen zu kennen, bevor Sie die lesen Ergebnisse.

Es gibt nur sehr wenige Umstände, unter denen die Zeilenanzahl tatsächlich vor dem Lesen der Ergebnismenge benötigt wird, insbesondere in einer Sprache wie Java. Der einzige Fall, an den ich denke, wo eine Zeilenanzahl erforderlich wäre, ist, wenn die Zeilenanzahl die einzigen Daten sind, die Sie benötigen (in diesem Fall wäre eine Zählabfrage überlegen). Andernfalls ist es besser, ein Wrapper-Objekt zur Darstellung Ihrer Tabellendaten zu verwenden und diese Objekte in einem dynamischen Container wie einer ArrayList zu speichern. Sobald die Ergebnismenge durchlaufen wurde, können Sie die Anzahl der Array-Listen abrufen. Für jede Lösung, bei der die Anzahl der Zeilen vor dem Lesen der Ergebnismenge bekannt sein muss, können Sie sich wahrscheinlich eine Lösung vorstellen, die dies tut, ohne die Anzahl der Zeilen vor dem Lesen ohne großen Aufwand zu kennen. Indem Sie an Lösungen denken, die die Notwendigkeit umgehen, die Zeilenanzahl vor der Verarbeitung zu kennen,

Jetzt sage ich natürlich nicht, dass es niemals Situationen gibt, in denen Sie möglicherweise die Zeilenanzahl benötigen, bevor Sie eine Ergebnismenge lesen. Ich sage nur, dass die Leute in den meisten Fällen, wenn sie glauben, dass sie die Anzahl der Ergebnisse benötigen, bevor sie sie lesen, dies wahrscheinlich nicht tun, und es lohnt sich, sich 5 Minuten Zeit zu nehmen, um darüber nachzudenken, ob es einen anderen Weg gibt.

Ich wollte nur meine 2 Cent zum Thema anbieten.

aeskreis
quelle
Um es kurz zu machen: Wenn man sowohl die Anzahl als auch die Elemente erhalten möchte, ist es vielleicht das Beste, Elemente rekursiv zu einer Liste hinzuzufügen (über den resultSet-Enumerator), dann die Anzahl abzurufen und die Liste anzuweisen, einem ein Array zu geben (falls erforderlich)
George Birbilis
1

Folgende zwei Optionen haben für mich funktioniert:

1) Eine Funktion, die die Anzahl der Zeilen in Ihrem ResultSet zurückgibt.

private int resultSetCount(ResultSet resultSet) throws SQLException{
    try{
        int i = 0;
        while (resultSet.next()) {
            i++;
        }
        return i;
    } catch (Exception e){
       System.out.println("Error getting row count");
       e.printStackTrace();
    }
    return 0;
}

2) Erstellen Sie eine zweite SQL-Anweisung mit der Option COUNT.

Nico
quelle
1

Das ResultSethat seine Methoden, die den Cursor abhängig von der bereitgestellten Option hin und her bewegen. Standardmäßig bewegt es sich vorwärts ( TYPE_FORWARD_ONLY ResultSetTyp). Wenn CONSTANTS nicht die Bildlauffähigkeit und Aktualisierung von ResultSetkorrekt anzeigt , wird möglicherweise ein Fehler angezeigt . ZB beforeLast() Diese Methode hat keine Auswirkung, wenn die Ergebnismenge keine Zeilen enthält. Wirft einen Fehler, wenn dies nicht der Fall ist TYPE_FORWARD_ONLY.

Der beste Weg, um zu überprüfen, ob leere Zeilen abgerufen wurden --- Nur um einen neuen Datensatz einzufügen, nachdem die Nichtexistenz überprüft wurde

if( rs.next() ) {

   Do nothing
} else {
  No records fetched!
}

Siehe hier

Yergalem
quelle
0

Hier ist ein Code, der verhindert, dass die Anzahl zum Instanziieren eines Arrays gezählt wird, aber stattdessen eine ArrayList verwendet und kurz vor der Rückkehr die ArrayList in den erforderlichen Array-Typ konvertiert.

Beachten Sie, dass die Supervisor-Klasse hier die ISupervisor-Schnittstelle implementiert, aber in Java können Sie nicht von object [] (die einfache toArray () -Methode von ArrayList) in ISupervisor [] umwandeln (wie ich denke, dass Sie dies in C # tun können) müssen alle Listenelemente durchlaufen und das Ergebnisarray füllen.

/**
 * Get Supervisors for given program id
 * @param connection
 * @param programId
 * @return ISupervisor[]
 * @throws SQLException
 */
public static ISupervisor[] getSupervisors(Connection connection, String programId)
  throws SQLException
{
  ArrayList supervisors = new ArrayList();

  PreparedStatement statement = connection.prepareStatement(SQL.GET_SUPERVISORS);
  try {
    statement.setString(SQL.GET_SUPERVISORS_PARAM_PROGRAMID, programId);
    ResultSet resultSet = statement.executeQuery();  

    if (resultSet != null) {
      while (resultSet.next()) {
        Supervisor s = new Supervisor();
        s.setId(resultSet.getInt(SQL.GET_SUPERVISORS_RESULT_ID));
        s.setFirstName(resultSet.getString(SQL.GET_SUPERVISORS_RESULT_FIRSTNAME));
        s.setLastName(resultSet.getString(SQL.GET_SUPERVISORS_RESULT_LASTNAME));
        s.setAssignmentCount(resultSet.getInt(SQL.GET_SUPERVISORS_RESULT_ASSIGNMENT_COUNT));
        s.setAssignment2Count(resultSet.getInt(SQL.GET_SUPERVISORS_RESULT_ASSIGNMENT2_COUNT));
        supervisors.add(s);
      }
      resultSet.close();
    }
  } finally {
    statement.close();
  }

  int count = supervisors.size();
  ISupervisor[] result = new ISupervisor[count];
  for (int i=0; i<count; i++)
    result[i] = (ISupervisor)supervisors.get(i);
  return result;
}
George Birbilis
quelle
In neueren Java-Versionen können Sie auch die Generics-Unterstützung im Compiler und zur Laufzeit verwenden, z. B. eine ArrayList <ISupervisor>, um zu vermeiden, dass das Ergebnis der toArray () -Methode umgewandelt wird (dh ISupervisor [] anstelle von object [] wird zurückgegeben) die Magie der Generika)
George Birbilis
Hinweis: Ich habe gerade "if" in "while" im obigen Beispiel geändert - Sie können "if" verwenden, wenn Sie nur 1 Zeile in den Ergebnissen erwarten
George Birbilis
-7

Von http://docs.oracle.com/javase/1.4.2/docs/api/java/sql/ResultSetMetaData.html

 ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM TABLE2");
 ResultSetMetaData rsmd = rs.getMetaData();
 int numberOfColumns = rsmd.getColumnCount();

Ein ResultSet enthält Metadaten, die die Anzahl der Zeilen angeben.

igor
quelle
10
Zeilen unterscheiden sich von Spalten. Die Metadaten enthalten keine Informationen zu Zeilen. Abgesehen vom Abrufen der Datenbank- und Tabellennamen geht es ausschließlich um Spalten.
Brad Mace