92

I'm trying to create a simple method that receives a ResultSet as a parameter and returns an int that contains the row count of the ResultSet. Is this a valid way of doing this or not so much?

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("--------------------------------------------------------");
    }

I tried this:

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

But I got an error saying

com.microsoft.sqlserver.jdbc.SQLServerException: 
The requested operation is not supported on forward only result sets.
cottontail
  • 10,268
  • 18
  • 50
  • 51
  • Can't you change sql statement to `SELECT COUNT(*) ...`? – Moyshe Oct 25 '11 at 08:20
  • 2
    Why you just don't perform a "select count(*)" before invoking the query filling the resultset ? However ForwardOnly RS depends on how you open connection (there's some params to pass). Look at your jdbc driver documentation for details. – BigMike Oct 25 '11 at 08:20
  • 1
    (OT; you might want to consider using an actual logging library that will take care of things like outputting the method name etc.) – Dave Newton Oct 25 '11 at 08:23

13 Answers13

74

If you have access to the prepared statement that results in this resultset, you can use

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

This prepares your statement in a way that you can rewind the cursor. This is also documented in the ResultSet Javadoc

In general, however, forwarding and rewinding cursors may be quite inefficient for large result sets. Another option in SQL Server would be to calculate the total number of rows directly in your SQL statement:

SELECT my_table.*, count(*) over () total_rows
FROM my_table
WHERE ...
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
  • I do have access to the prepared statement, but am in no way really competent in SQL. The current prepared statement looks like this: "Select m.* from [Messages] m INNER JOIN RequestMessageIndex i on m.messageGUID = i.MessageGuid where i.requestfield = 'DR' and i.indexValue like '%TAGER00%'" So do I just have to add some code to the SQL statement or create another statement? Sorry, I'm really dumb when it comes to SQL... –  Oct 25 '11 at 08:40
  • 1
    @DeanGrobler: There's nothing wrong about asking these things! Both options I mentioned can be valid. In any case, counting the number of rows can impact performance with either technique. I personally prefer the second one, using a calculated `count(*) over (partition by 1)` field, but only if you can use that count for things like paging in a GUI. If this is only about logging, then I'm not sure if generally getting the row count is a good idea... – Lukas Eder Oct 25 '11 at 08:43
  • This is deff necessary to get the row count of the resultset, the variable is to be used to construct a dynamic table around the returned data for the GUI, so deff going to have to do it. On the plus side, the data that is being returned is from a search function. So the resultset rows would only be about 15 rows max. –  Oct 25 '11 at 08:53
  • @DeanGrobler: If you know you're talking about 15 rows max, then you're on the safe side with both options. – Lukas Eder Oct 25 '11 at 09:00
  • Way to complicated of an answer....rs = st.executeQuery("SELECT COUNT(*) FROM foo"); rs.next(); int rowCount = rs.getInt(1); – Jeryl Cook Oct 10 '18 at 17:08
  • @JerylCook: The point is that sometimes, you don't want to run two queries to get the additional count value – Lukas Eder Oct 10 '18 at 17:51
  • the question "How to get row count using ResultSet in Java?" , he should simply do a select count(*), and get the results....that's really it. – Jeryl Cook Oct 10 '18 at 18:06
  • 1
    @JerylCook: There is a pre-existing `ResultSet` and they want to count the rows on that result set. What am I missing? *"receives a ResultSet as a parameter and returns an int that contains the row count of the ResultSet"*. My answer showed their mistake (not specifying `ResultSet.TYPE_SCROLL_INSENSITIVE`) and showed an alternative. So, what exactly are you criticising? Do note there are other answers that suggest `SELECT count(*)` queries... – Lukas Eder Oct 11 '18 at 06:31
  • I was to harsh in my wording... I get why you answered and the explanation. @LukasEder – Jeryl Cook Oct 11 '18 at 14:45
  • 1
    @JerylCook: Cheers :-) – Lukas Eder Oct 11 '18 at 15:52
48
Statement s = cd.createStatement();
ResultSet r = s.executeQuery("SELECT COUNT(*) AS recordCount FROM FieldMaster");
r.next();
int count = r.getInt("recordCount");
r.close();
System.out.println("MyTable has " + count + " row(s).");

Sometimes JDBC does not support following method gives Error like `TYPE_FORWARD_ONLY' use this solution

Sqlite does not support in JDBC.

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

So at that time use this solution.

user3267567
  • 524
  • 4
  • 14
Java Man
  • 1,854
  • 3
  • 21
  • 43
  • thank you, this is correct explanation and correct solution. – Sambuu Apr 22 '20 at 06:28
  • Had to adjust this answer for Apache Derby database via JDBC as follows: `Statement s = cd.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);` – MikeOnline Feb 11 '22 at 09:08
40

your sql Statement creating code may be like

statement = connection.createStatement();

To solve "com.microsoft.sqlserver.jdbc.SQLServerException: The requested operation is not supported on forward only result sets" exception, change above code with

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

After above change you can use

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

to get row count

cottontail
  • 10,268
  • 18
  • 50
  • 51
Fathah Rehman P
  • 8,401
  • 4
  • 40
  • 42
8

I just made a getter method.

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
  • 81
  • 1
  • 1
7

Do a SELECT COUNT(*) FROM ... query instead.

4

If you have table and are storing the ID as primary and auto increment then this will work

Example code to get the total row count http://www.java2s.com/Tutorial/Java/0340__Database/GettheNumberofRowsinaDatabaseTable.htm

Below is 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", "");
  }
}
cottontail
  • 10,268
  • 18
  • 50
  • 51
anand
  • 392
  • 3
  • 8
4

Most drivers support forward only resultset - so method like last, beforeFirst etc are not supported.

The first approach is suitable if you are also getting the data in the same loop - otherwise the resultSet has already been iterated and can not be used again.

In most cases the requirement is to get the number of rows a query would return without fetching the rows. Iterating through the result set to find the row count is almost same as processing the data. It is better to do another count(*) query instead.

gkamal
  • 20,777
  • 4
  • 60
  • 57
  • On some database systems it can be very slow to do a select count(*) query, so maybe the TS should find a way to remove the need to know the size of the resultset at all. – Mark Rotteveel Oct 25 '11 at 08:38
  • I'm pretty sure the SQL Server JDBC driver supports rewindable result sets? It's a matter of correctly initialising the prepared statement... And I agree with @MarkRotteveel, issuing two queries just to get a row count can be fatal. If it is really needed, you can always add a `count(*) over (partition by 1)` window function, even if this can be slow for large result sets as well... – Lukas Eder Oct 25 '11 at 08:39
2

Others have already answered how to solve your problem, so I won't repeat what has already been said, but I will says this: you should probably figure out a way to solve your problems without knowing the result set count prior to reading through the results.

There are very few circumstances where the row count is actually needed prior to reading the result set, especially in a language like Java. The only case I think of where a row count would be necessary is when the row count is the only data you need(in which case a count query would be superior). Otherwise, you are better off using a wrapper object to represent your table data, and storing these objects in a dynamic container such as an ArrayList. Then, once the result set has been iterated over, you can get the array list count. For every solution that requires knowing the row count before reading the result set, you can probably think of a solution that does so without knowing the row count before reading without much effort. By thinking of solutions that bypass the need to know the row count before processing, you save the ResultSet the trouble of scrolling to the end of the result set, then back to the beginning (which can be a VERY expensive operation for large result sets).

Now of course I'm not saying there are never situations where you may need the row count before reading a result set. I'm just saying that in most circumstances, when people think they need the result set count prior to reading it, they probably don't, and it's worth taking 5 minutes to think about whether there is another way.

Just wanted to offer my 2 cents on the topic.

aeskreis
  • 1,923
  • 2
  • 17
  • 24
  • to keep it short: if one wants to get both count and items the best thing is maybe to add items recursively to a list (via the resultSet enumerator) then get the count and tell the list to give one an array (if needed) – George Birbilis Jul 24 '13 at 10:29
2

Your function will return the size of a ResultSet, but its cursor will be set after last record, so without rewinding it by calling beforeFirst(), first() or previous() you won't be able to read its rows, and rewinding methods won't work with forward only ResultSet (you'll get the same exception you're getting in your second code fragment).

socha23
  • 10,171
  • 2
  • 28
  • 25
1

Following two options worked for me:

1) A function that returns the number of rows in your ResultSet.

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) Create a second SQL statement with the COUNT option.

Nico
  • 216
  • 1
  • 5
1

The ResultSet has it's methods that move the Cursor back and forth depending on the option provided. By default, it's forward moving(TYPE_FORWARD_ONLY ResultSet type). Unless CONSTANTS indicating Scrollability and Update of ResultSet properly, you might end up getting an error. E.g. beforeLast() This method has no effect if the result set contains no rows. Throws Error if it's not TYPE_FORWARD_ONLY.

The best way to check if empty rows got fetched --- Just to insert new record after checking non-existence

if( rs.next() ) {

   Do nothing
} else {
  No records fetched!
}

See here

Yousha Aleayoub
  • 4,532
  • 4
  • 53
  • 64
Yergalem
  • 1,603
  • 18
  • 14
0

Here's some code that avoids getting the count to instantiate an array, but uses an ArrayList instead and just before returning converts the ArrayList to the needed array type.

Note that Supervisor class here implements ISupervisor interface, but in Java you can't cast from object[] (that ArrayList's plain toArray() method returns) to ISupervisor[] (as I think you are able to do in C#), so you have to iterate through all list items and populate the result array.

/**
 * 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
  • 2,782
  • 2
  • 33
  • 35
  • in newer versions of Java you can also use Generics support in the compiler and runtime, like an ArrayList to avoid casting the result of the toArray() method (it will return ISupervisor[] instead of object[] then, that is the magic of Generics) – George Birbilis Jul 24 '13 at 10:48
  • note I just changed "if" to "while" in the sample above - you can use "if" when you're expecting just 1 row in results – George Birbilis Jul 25 '13 at 12:02
-6

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

A ResultSet contains metadata which gives the number of rows.

igor
  • 33
  • 10
    Rows are different than columns. The meta data doesn't contain any information pertaining to rows. Aside from getting the database and table names it is exclusively about columns. – Brad Mace Jul 27 '12 at 17:47