如何在Java中使用ResultSet获取行数?

92
我正在尝试创建一个简单的方法,它接收一个ResultSet作为参数,并返回一个包含ResultSet行数的int。这是有效的方法吗?
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("--------------------------------------------------------");
    }

我尝试了这个:

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

但我收到了一个错误信息

com.microsoft.sqlserver.jdbc.SQLServerException: 
The requested operation is not supported on forward only result sets.

你不能将 SQL 语句更改为 SELECT COUNT(*) ... 吗? - Moyshe
2
为什么你不在调用填充结果集的查询之前执行一个 "select count(*)"?但是 ForwardOnly RS 取决于你如何打开连接(需要传递一些参数)。请查看你的 JDBC 驱动程序文档以获取详细信息。 - BigMike
1
(注:你可能想考虑使用一个真正的日志库,它会处理输出方法名称等事项。) - Dave Newton
13个回答

74
如果您可以访问导致此结果集的预处理语句,则可以使用。
connection.prepareStatement(sql, 
  ResultSet.TYPE_SCROLL_INSENSITIVE, 
  ResultSet.CONCUR_READ_ONLY);

这将以一种可以倒带光标的方式准备您的语句。这在ResultSet Javadoc中也有记录。

然而,对于大型结果集,前进和倒带光标可能非常低效。在SQL Server中的另一个选择是直接在您的SQL语句中计算总行数:

SELECT my_table.*, count(*) over () total_rows
FROM my_table
WHERE ...

我确实可以访问准备好的语句,但在 SQL 方面并不真正熟练。当前的准备好的语句看起来像这样: "Select m.* from [Messages] m INNER JOIN RequestMessageIndex i on m.messageGUID = i.MessageGuid where i.requestfield = 'DR' and i.indexValue like '%TAGER00%'" 那么我只需要在 SQL 语句中添加一些代码还是创建另一个语句?很抱歉,当涉及到 SQL 时,我确实很愚蠢... - user818700
1
@DeanGrobler:询问这些问题没有任何问题!我提到的两个选项都是有效的。无论哪种技术,计算行数都会影响性能。我个人更喜欢第二种方法,使用计算的 count(*) over (partition by 1) 字段,但仅当您可以将该计数用于诸如 GUI 中的分页等操作时才使用。如果只是关于日志记录,那么我不确定通常获取行数是否是一个好主意... - Lukas Eder
这是获取结果集行数所必需的,该变量将用于构建围绕GUI返回数据的动态表格,因此必须执行。好的一面是,返回的数据来自搜索功能。因此,结果集行最多只有15行。 - user818700
1
@JerylCook:有一个现成的 ResultSet,他们想要对该结果集进行行计数。我错过了什么? "接收一个 ResultSet 作为参数,并返回包含 ResultSet 行计数的 int". 我的回答显示了他们的错误(没有指定 ResultSet.TYPE_SCROLL_INSENSITIVE),并展示了一种替代方案。那么,你到底是在批评什么?请注意,还有其他答案建议使用 SELECT count(*) 查询... - Lukas Eder
1
@JerylCook:干杯 :-) - Lukas Eder
显示剩余5条评论

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

有时候JDBC不支持以下方法会出错,例如 `TYPE_FORWARD_ONLY' ,可以使用这个解决方案。

JDBC不支持Sqlite。

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

所以在那个时候使用这个解决方案。


谢谢,这是正确的解释和正确的解决方案。 - Sambuu
不得不通过JDBC调整这个Apache Derby数据库的答案,如下所示:Statement s = cd.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - MikeOnline

40

你的 SQL 语句创建代码可能如下所示

statement = connection.createStatement();

为解决"com.microsoft.sqlserver.jdbc.SQLServerException: The requested operation is not supported on forward only result sets"异常,请使用以下代码:

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

在以上更改后,你可以使用

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

获取行数


8
我刚刚创建了一个获取方法。
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;
}

7
你是指为了获取数量而执行整个查询?最好动态编辑SQL查询,使其仅返回COUNT。 - George Birbilis

7

建议使用SELECT COUNT(*) FROM ...查询语句。


4
如果您有表格,将ID存储为主键和自动递增,则此方法适用。
获取总行数的示例代码http://www.java2s.com/Tutorial/Java/0340__Database/GettheNumberofRowsinaDatabaseTable.htm 以下是代码。
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", "");
  }
}

4
大多数驱动程序仅支持向前的结果集 - 因此像last、beforeFirst等方法不受支持。
如果您在同一循环中获取数据,则第一种方法是合适的——否则,resultSet已经被迭代并且无法再次使用。
在大多数情况下,要求获取一个查询返回的行数而不获取行。通过遍历结果集来查找行数几乎与处理数据相同。最好执行另一个count(*)查询。

在某些数据库系统中,执行select count(*)查询可能非常缓慢,因此TS可能需要找到一种方法来消除完全了解结果集大小的必要性。 - Mark Rotteveel
我非常确定SQL Server JDBC驱动程序支持可倒带的结果集?这只是正确初始化预处理语句的问题... 我同意@MarkRotteveel的观点,发出两个查询只是为了获取行数可能是致命的。如果真的需要,您可以始终添加一个count(*) over (partition by 1)窗口函数,即使对于大型结果集来说这也可能很慢... - Lukas Eder

2
其他人已经回答了如何解决您的问题,所以我不会重复已经说过的内容,但我想说的是:您应该找到一种在阅读结果之前解决问题的方法,而不需要知道结果集计数。
在很少的情况下,实际上需要在阅读结果集之前知道行数,特别是在像Java这样的语言中。我想到的唯一情况是当行数是您所需唯一数据时(在这种情况下,计数查询将更优)。否则,最好使用包装对象来表示您的表数据,并将这些对象存储在动态容器中,例如ArrayList。然后,一旦遍历了结果集,您可以获取数组列表计数。对于每个需要在阅读结果集之前知道行数的解决方案,您都可以轻松想到一种不需要在阅读结果之前知道行数的解决方案。通过思考绕过处理之前必须知道行数的解决方案,您可以节省ResultSet滚动到结果集的末尾,然后返回到开头的麻烦(对于大型结果集,这可能是一个非常昂贵的操作)。
现在当然我并不是说永远没有需要在读取结果集之前知道行数的情况。我只是说,在大多数情况下,当人们认为他们需要在读取结果之前知道结果集计数时,他们可能不需要,并且值得花5分钟思考是否有另一种方法。
只是想提供我的两分钱意见。

简而言之:如果想同时获取计数和项目,则最好通过将项目递归添加到列表中(通过resultSet枚举器),然后获取计数并告诉列表提供一个数组(如果需要)。 - George Birbilis

2

你的函数将返回ResultSet的大小,但是它的光标将被设置在最后一条记录之后,所以如果不通过调用beforeFirst()、first()或previous()来将其倒回,你将无法读取它的行,并且倒带方法将无法与只向前的ResultSet一起使用(你会得到与第二个代码片段中相同的异常)。


1
以下两个选项对我有用:
1)一个返回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)创建第二个带有COUNT选项的SQL语句。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接