Problem:
I get large ResultSet from DB(MySQL)(more than 1 000 000 rows) and handle each row about 40 seconds.
Summary, i work with ResultSet more than 30 minutes, i get less records than really contain in database table аnd have no errors and no warrnings. If i count quantity of rows of that ResultSet, it's allright(quantity of ResultSet = quantity of DB).
Is some limitations of mysql server or mysql jdbc driver or something else?
My code. it works in spring framework :
public void query(String query, RowCallbackHandler rowCallbackHandler) throws SQLException {
ResultSet rs = null;
ResultSet rsCount = null;
Statement stmt = null;
Statement stmtCount = null;
try {
stmt = createStatmant();
rs = stmt.executeQuery(query);
if (rs == null) {
log.info("result set is null");
}
stmtCount = createStatmant();
rsCount = stmtCount.executeQuery(query);
int i = 0;
while(rsCount.next()){
i++;
}
log.info("ResultSet size : "+i);
int j = 0;
rs.next();
do{
j++;
rowCallbackHandler.processRow(rs);
}while (rs.next());
log.info("ResultSet size real : "+i);
log.info("ResultSet size fact : "+j);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage());
}finally{
if(stmt!=null){
stmt.close();
}
if(rs!=null){
rs.close();
}
if(rsCount!=null){
rsCount.close();
}
}
}
createStatment :
private Statement createStatmant() throws SQLException {
((BasicDataSource)dataSource).setTimeBetweenEvictionRunsMillis(1000*60*60);
Statement stmt = dataSource.getConnection().createStatement(
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(fetchSize);
stmt.setMaxRows(50000000-1);
return stmt;
}
datasource is global variable in class:
private javax.sql.DataSource dataSource;
You're handing a mutable object, rs, back to your callbackHandler. Make sure it's not advancing or closing the ResultSet.
You declare four objects that can be closed at the top of your method, but only close three at the bottom. Don't forget to close stmtCount.
I'm assuming you're performing that count as a troubleshooting approach for our benefit, and that you're not doing that in production.
1 Million rows times 40 Seconds = 40 Million Seconds = 463 days. The math doesn't support the statement that you "handle each row about 40 seconds".
Is anything externally writing to the database as you're reading?
If you're able to compare counts from your log, it means you haven't thrown an Exception. That's a useful clue.
Related
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.
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 ...
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.
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
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;
}
Do a SELECT COUNT(*) FROM ... query instead.
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.
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", "");
}
}
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).
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.
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.
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
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;
}
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.
executor = Executors.newScheduledThreadPool(1);
Runnable helloRunnable = new Runnable() {
public void run() {
controller2.GetAIntFromDatabase(columblName);
}
};
executor.scheduleAtFixedRate(helloRunnable, 0, 10, TimeUnit.MILLISECONDS);
this part of the program generates the increasing heap memory error.
controller2.GetAIntFromDatabase(columblName);
with this function I read an int value from my database.
#Override
public int GetAIntFromDatabase(String columblName) {
int stare = 0;
try{
String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
PreparedStatement preparedStatement = this.connnection.prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()){
stare = resultSet.getInt(columblName);
preparedStatement.close();
resultSet.close();
return stare;
}
preparedStatement.close();
resultSet.close();
}catch (SQLException ex) {
System.out.println("GetUtilajStare Error: " + ex);
return 0;
}
return 0;
}
That's the Java heap memory usage after 10 minutes of running:
Why does my heap memory keep increasing?
If an exception is thrown after opening the preparedStatement and the resultSet, they will never be closed. Therefore you should use the finally block which will always be executed.
public int GetAIntFromDatabase(String columblName) {
int stare = 0;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
preparedStatement = this.connnection.prepareStatement(query);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
stare = resultSet.getInt(columblName);
return stare;
}
} catch (SQLException ex) {
System.out.println("GetUtilajStare Error: " + ex);
return 0;
} finally {
if (preparedStatement != null)
preparedStatement.close();
if (resultSet != null)
resultSet.close();
}
return 0;
}
You should use a Java 7+ way of closing the resources, try-with-resources.
The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements AutoCloseable, which includes all objects which implement Closeable, can be used as a resource.
public int GetAIntFromDatabase(String columblName) {
final String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
try (final PreparedStatement preparedStatement = this.connnection.prepareStatement(query)) {
final ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
return resultSet.getInt(columblName);
}
return 0;
} catch (SQLException ex) {
// Do something better than System.out.println(...)
return 0;
}
return 0;
}
Also you do not have to explicitly close the result set, the prepared statement does it as it owns the result set:
When a Statement object is closed, its current ResultSet object, if one exists, is also closed.
However, if you want to be paranoid and overkill it, as #MarkRotteveel suggests in his comment, you may add as an AutoCloseable resource also the ResultSet, and the code would then look like this:
public int GetAIntFromDatabase(String columblName) {
final String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
try (final PreparedStatement preparedStatement = this.connnection.prepareStatement(query);
final ResultSet resultSet = preparedStatement.executeQuery()
) {
...
}
I have never done it and never needed it and the documentation explicitly states that it's not necessary, but some people have experienced problems in certain edge cases.
In this second case it's especially visible how much the try-with-resources saves you from - you do not need to assign the variables to null, you do not have to check if any of them have been opened, and even if one of the close() methods throws an exception, the "closing chain" is not broken and the close() methods of the other resources are called in any case.
I made a java method to add a row to a database. I am calling this method about 1000 plus times for testing purposes. I called the close() method on my prepared statement and i am still getting the oracle error whenever this method is called to insert a row.
Error
ORA-01000: maximum open cursors exceeded
Source Code
public void insertARow(ArrayList<String> row)
{
try
{
//Proper SQL statement here, checked by running on DB
String insert = "INSERT INTO user.info(cola,colb) values(?,?)";
//Add a row
PreparedStatement ps = con.prepareStatement(insert);//con is a connection object
//'row' is an arraylist of strings
for(int i = 0; i < row.size(); i++ )
{
int j = 1 + i ;
String temp = row.get(i);
ps.setString(j , temp);
}
ps.executeUpdate();//The reason for problems !!!
ps.close();
}catch(SQLException e)
{
System.out.println("Cannot add row !");
e.printStackTrace();
}
}
If you're trying to perform the same operation a 1000 times, I would advise re-using the same PreparedStatement or using addBatch() and executeBatch() combo.
If you're planning to re-use your PreparedStatement, here's something that you can do:
public void insertARow(PreparedStatement ps, ArrayList<String> row){
//your code
}
public void calledMethod(){
String insert = "INSERT INTO user.info(cola,colb) values(?,?)";
PreparedStatement ps = null;
try{
ps = con.prepareStatement(insert);
/**
* Here you make the call to insertARow passing it the preparedstatement that you
* have created. This in your case will be called multiple times.
*/
insertARow(ps, row);
}finally{
if(ps != null){
//close ps
}
}
}
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.
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 ...
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.
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
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;
}
Do a SELECT COUNT(*) FROM ... query instead.
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.
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", "");
}
}
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).
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.
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.
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
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;
}
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.
How to check if resultset has one row or more with JDBC?
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
boolean isMoreThanOneRow = rs.first() && rs.next();
You didn't ask this one, but you may need it:
boolean isEmpty = ! rs.first();
Normally, we don't need the row count because we use a WHILE loop to iterate through the result set instead of a FOR loop:
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (rs.next()) {
// retrieve and print the values for the current row
int i = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
System.out.println("ROW = " + i + " " + s + " " + f);
}
However, in some cases, you might want to window the results, and you need the record count ahead of time to display to the user something like Row 1 to 10 of 100. You can do a separate query with SELECT COUNT(*) first, to get the record count, but note that the count is only approximate, since rows can be added or removed between the time it takes to execute the two queries.
Sample from ResultSet Overview
There are many options, and since you don't provide more context the only thing left is to guess. My answers are sorted by complexity and performance ascending order.
Just run select count(1) FROM ... and get the answer. You'd have to run another query that actually selects and returns the data.
Iterate with rs.next() and count until you're happy. Then if you still need the actual data re-run same query.
If your driver supports backwards iteration, go for rs.next() couple of times and then rewind back with rs.previous().
You don't need JDBC for this. The normal idiom is to collect all results in a collection and make use of the collection methods, such as List#size().
List<Item> items = itemDAO.list();
if (items.isEmpty()) {
// It is empty!
if (items.size() == 1) {
// It has only one row!
} else {
// It has more than one row!
}
where the list() method look like something:
public List<Item> list() throws SQLException {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
List<Item> items = new ArrayList<Item>();
try {
connection = database.getConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery(SQL_LIST);
while (resultSet.next()) {
Item item = new Item();
item.setId(resultSet.getLong("id"));
item.setName(resultSet.getString("name"));
// ...
items.add(item);
}
} finally {
if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {}
if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
}
return items;
}
If you want to make sure that there is exactly one row, you can ensure that the first row is the last:
ResultSet rs = stmt.executeQuery("SELECT a FROM Table1 WHERE b=10");
if (rs.isBeforeFirst() && rs.next() && rs.isFirst() && rs.isLast()) {
// Logic for where there's exactly 1 row
Long valA = rs.getLong("a");
// ...
}
else {
// More that one row or 0 rows returned.
// ..
}
My no-brainer suggestion: Fetch the first result row, and then try to fetch the next. If the attempt is successful, you have more than one row.
If there is more than one row and you want to process that data, you'll need to either cache the stuff from the first row, or use a scrollable result set so you can seek back to the top before going through the results.
You can also ask SQL directly for this information by doing a SELECT COUNT(*) on the rest of your query; the result will be 0, 1 or more depending on how many rows the rest of the query would return. That's pretty easy to implement but involves two queries to the DB, assuming you're going to want to read and process the actual query next.
This implementation allows you to check for whether result of the query is empty or not at the cost of duplicating some lines.
ResultSet result = stmt.executeQuery("SELECT * FROM Table");
if(result.next()) {
// Duplicate the code which should be pasted inside while
System.out.println(result.getInt(1));
System.out.println(result.getString(2));
while(result.next()){
System.out.println(result.getInt(1));
System.out.println(result.getString(2));
}
}else{
System.out.println("Query result is empty");
}
Drawbacks:
In this implementation a portion of the code will be duplicated.
You cannot know how many lines are present in the result.
Get the Row Count using ResultSetMetaData class.
From your code u can create ResultSetMetaData like :
ResultSetMetaData rsmd = resultSet.getMetaData(); //get ResultSetMetaData
rsmd.getColumnCount(); // get row count from resultsetmetadata