【发布时间】:2011-02-05 05:04:01
【问题描述】:
如何使用 JDBC 检查结果集是否有一行或多行?
【问题讨论】:
-
这确实是 JDBC 中缺少的东西,原因是并非所有数据库系统都支持提前获取结果集的大小(因为结果不是预取的)。不幸的是,这意味着您无法在支持它的数据库(例如 MySQL)中轻松使用这些功能。
如何使用 JDBC 检查结果集是否有一行或多行?
【问题讨论】:
我的明智建议:获取第一个结果行,然后尝试获取下一个结果行。如果尝试成功,则您有不止一行。
如果有不止一行,并且您想要处理该数据,则需要缓存第一行中的内容,或者使用可滚动的结果集,以便您可以在通过结果。
您还可以通过在查询的其余部分执行SELECT COUNT(*) 来直接向 SQL 询问此信息;结果将是 0、1 或更多,具体取决于查询的其余部分将返回多少行。这很容易实现,但需要对数据库进行两次查询,假设您接下来要读取和处理实际查询。
【讨论】:
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
boolean isMoreThanOneRow = rs.first() && rs.next();
你没有问这个,但你可能需要它:
boolean isEmpty = ! rs.first();
通常,我们不需要行数,因为我们使用 WHILE 循环而不是 FOR 循环来迭代结果集:
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);
}
但是,在某些情况下,您可能希望将结果窗口化,并且您需要提前将记录计数显示给用户,例如 Row 1 to 10 of 100。您可以先使用SELECT COUNT(*) 进行单独查询,以获取记录计数,但请注意,该计数只是近似值,因为可以在执行两个查询的时间之间添加或删除行。
来自ResultSet Overview的样本
【讨论】:
executeQuery 语句之后立即执行此操作,这样first() 就不需要倒带或更改您的光标,以便它可以倒带。跨度>
有很多选择,由于您没有提供更多上下文,所以剩下的就是猜测。我的答案按复杂性和性能升序排序。
select count(1) FROM ... 即可获得答案。您必须运行另一个实际选择并返回数据的查询。rs.next() 进行迭代并数数,直到您满意为止。然后,如果您仍然需要实际数据,请重新运行相同的查询。rs.next() 几次,然后使用rs.previous() 倒回。【讨论】:
first() 通常可以重新开始。
您不需要 JDBC。通常的习惯用法是将所有结果收集到一个集合中,并利用集合方法,例如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!
}
list() 方法看起来像什么:
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;
}
【讨论】:
SELECT * FROM table 他们已经没有意义了。那么你需要SELECT COUNT(*) FROM table。 JDBC 根本不是用于此特定目的的正确工具,这正是 JDBC 中不存在虚构的 ResultSet#size() 方法的原因。
SELECT * FROM table 我也没有建议过。最初的问题并没有假设所有数据实际上也是必要的。
使用ResultSetMetaData 类获取行数。
您可以从您的代码中创建ResultSetMetaData,例如:
ResultSetMetaData rsmd = resultSet.getMetaData(); //get ResultSetMetaData
rsmd.getColumnCount(); // get row count from resultsetmetadata
【讨论】:
如果要确保正好一行,可以确保第一行是最后一行:
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.
// ..
}
【讨论】:
rs.next() 已经跳到下一个结果集,它会给你错误的数据。我在 join 子句上对其进行了测试,它删除了一个元素
此实现允许您检查查询结果是否为空,但代价是复制了一些行。
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");
}
缺点:
- 在此实现中,部分代码将被复制。
- 您无法知道结果中有多少行。
【讨论】: