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


75

我正在尝试创建一个简单的方法,该方法接收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 2011年

2
为什么在调用填充结果集的查询之前不执行“选择计数(*)”?但是ForwardOnly RS取决于打开连接的方式(有一些参数要传递)。有关详细信息,请参见您的jdbc驱动程序文档。
BigMike 2011年

1
(OT;您可能要考虑使用一个实际的日志记录库,该库将处理诸如输出方法名称之类的事情。)
Dave Newton

Answers:


67

如果您有权访问导致该结果集中的准备好的语句,则可以使用

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。当前准备的语句看起来像这样:“从[消息]中选择m。*。我是否只需要向SQL语句中添加一些代码或创建另一个语句?抱歉,在SQL方面我真的

1
@DeanGrobler:问这些事情没有错!我提到的两个选项都是有效的。在任何情况下,对行数进行计数都会影响这两种技术的性能。我个人更喜欢使用计算count(*) over (partition by 1)字段的第二个字段,但前提是您可以使用该计数来进行GUI分页等操作。如果这仅与日志记录有关,那么我不确定通常获取行数是否是个好主意……
Lukas Eder

这对于获得结果集的行数是必需的,该变量将用于围绕GUI返回的数据构造一个动态表,因此deff将必须这样做。从正面看,返回的数据来自搜索功能。因此,结果集行最多只能有15行左右。

1
@JerylCook:有一个预先存在的对象ResultSet,他们想计算该结果集上的行。我想念什么?“接收ResultSet作为参数,并返回一个包含ResultSet的行数的int”。我的回答显示了他们的错误(未指定ResultSet.TYPE_SCROLL_INSENSITIVE),并显示了替代方法。那么,您究竟在批评什么?请注意,还有其他一些建议会提出SELECT count(*)疑问……
Lukas Eder

1
@JerylCook:干杯:-)
卢卡斯·埃德

37

您的sql语句创建代码可能像

statement = connection.createStatement();

解决“ com.microsoft.sqlserver.jdbc.SQLServerException:仅转发结果集不支持所请求的操作”的异常更改,上面的代码带有

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;

获取行数


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

有时JDBC不支持以下方法给出错误,例如“ TYPE_FORWARD_ONLY”,使用此解决方案

Sqlite在JDBC中不支持。

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

因此,当时使用此解决方案。

谢谢..


谢谢,这是正确的解释和正确的解决方案。
桑布

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


3

如果您有表格并将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", "");
  }
}

2

您的函数将返回ResultSet的大小,但是它将在最后一条记录之后设置其光标,因此如果不通过调用beforeFirst(),first()或previous()来回退它,则将无法读取其行并回退方法仅适用于前进的ResultSet(在第二个代码片段中将获得相同的异常)。


2

大多数驱动程序仅支持转发结果集-因此不支持诸如last,beforeFirst等方法。

如果您也要在同一循环中获取数据,则第一种方法是合适的-否则resultSet已经被迭代并且不能再次使用。

在大多数情况下,要求是获取查询将返回的行数,而无需获取行。遍历结果集以查找行数与处理数据几乎相同。最好改为执行另一个count(*)查询。


在某些数据库系统上,执行select count(*)查询可能非常慢,因此,也许TS应该找到一种方法,根本不需要知道结果集的大小。
Mark Rotteveel 2011年

我很确定SQL Server JDBC驱动程序支持可倒带的结果集?这是正确初始化准备好的语句的问题……我同意@MarkRotteveel的观点,发出两个查询只是为了获取行数可能是致命的。如果确实需要,count(*) over (partition by 1)即使对于大型结果集也可能很慢,您也可以随时添加窗口函数...
Lukas Eder

2

其他人已经回答了如何解决您的问题,所以我不再重复已经说过的话,但是我会这样说:您可能应该在不通读结果集计数的情况下,想出一种解决问题的方法结果。

在极少数情况下,在读取结果集之前实际需要行计数,尤其是在Java之类的语言中。我认为唯一需要行计数的情况是,当行计数是您唯一需要的数据时(在这种情况下,计数查询会更好)。否则,最好使用包装对象来表示表数据,并将这些对象存储在动态容器(例如ArrayList)中。然后,一旦迭代了结果集,就可以获取数组列表计数。对于需要在读取结果集之前知道行数的每个解决方案,您可能会想到这样的解决方案,而无需在读取结果之前就知道行数而无需付出太多努力。通过考虑一些解决方案,这些解决方案无需在处理之前就知道行数,

现在,我当然不是在任何情况下都不会在读取结果集之前需要行计数的情况。我只是说,在大多数情况下,当人们认为在读取结果集之前需要结果集计数时,他们可能并不需要,而花5分钟的时间思考是否存在另一种方法是值得的。

只是想提供我关于这个主题的2美分。


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

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语句。


1

ResultSet具有根据提供的选项来回移动光标的方法。默认情况下,它是前向移动(TYPE_FORWARD_ONLY ResultSet类型)。除非CONSTANTSResultSet正确指示可滚动性和更新,否则您可能最终会收到错误消息。例如,beforeLast() 如果结果集不包含任何行,则此方法无效。如果不是则抛出错误TYPE_FORWARD_ONLY

检查是否获取空行的最佳方法---仅在检查不存在后插入新记录

if( rs.next() ) {

   Do nothing
} else {
  No records fetched!
}

这里


0

以下是一些代码,这些代码避免获取实例化数组的计数,而是使用ArrayList,并且在返回之前将ArrayList转换为所需的数组类型。

请注意,这里的Supervisor类实现了ISupervisor接口,但是在Java中,您无法将object [](ArrayList的纯toArray()方法返回)转换为ISupervisor [](因为我认为您可以在C#中完成此操作),必须遍历所有列表项并填充结果数组。

/**
 * 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;
}

在较新版本的Java中,您还可以在编译器和运行时中使用泛型支持,例如ArrayList <ISupervisor>以避免强制转换toArray()方法的结果(它将返回ISupervisor []而不是object [],即泛型魔术)
George Birbilis

请注意,我只是在上面的示例中将“ if”更改为“ while”-当您期望结果仅排成一行时,可以使用“ if”
George Birbilis 2013年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.