我有一张包含数百万条记录的表格。为了使系统更快,我需要在我的Java代码中实现分页概念。我需要一次只获取1000条记录并处理它们,然后选择另外1000条记录并进行处理等等。我已经尝试了一些东西,但没有一个在起作用。我试过的一些事情列在下面 -
1) String query = "select * from TABLENAME" + " WHERE ROWNUM BETWEEN %d AND %d";
sql = String.format(query, firstrow, firstrow + rowcount);
在上面的示例中,当查询为SELECT * from TABLENAME Where ROWNUM BETWEEN 0 and 10
时,它会给我一个结果,但当查询为SELECT * from TABLENAME Where ROWNUM BETWEEN 10 and 20
时,它会返回一个空结果集。我甚至试图在DB中运行它,它返回Empty结果集(不知道为什么!!)
2)preparedStatement.setFetchSize(100);
我在我的Java代码中有这个,但它仍然从表中获取所有记录。无论如何,添加此语句并不会影响我的代码。
请帮忙!
答案 0 :(得分:6)
听起来您实际上并不需要对结果进行分页,而只是批量处理结果。如果是这种情况,那么您需要做的就是使用setFetchSize将获取大小设置为1000并像往常一样迭代结果集(使用resultset.next()
)并在迭代时处理结果。有许多资源描述了setFetchSize及其功能。做一些研究:
对于oracle分页,有很多资源描述了如何执行此操作。只需进行网络搜索。以下是一些描述如何操作的资源:
如果您没有定义一致的排序(ORDER BY
子句),则分页不是很有用,因为您不能依赖它们返回的顺序。
这个答案解释了为什么BETWEEN
声明不起作用:https://stackoverflow.com/a/10318244/908961
如果使用超过12c的oracle,您需要进行子选择以获得结果。类似的东西:
SELECT c.*
FROM (SELECT c.*, ROWNUM as rnum
FROM (SELECT * FROM TABLENAME ORDER BY id) c) c
WHERE c.rnum BETWEEN %d AND %d
如果您使用的是Oracle 12c或更高版本,我建议您使用较新的OFFSET
FETCH
语法,而不是摆弄rownum
。请参阅上面的第一个链接或
http://www.toadworld.com/platforms/oracle/b/weblog/archive/2016/01/23/oracle-12c-enhanced-syntax-for-row-limiting-a-k-a-top-n-queries
所以你的查询就像
String query = "select * from TABLENAME OFFSET %d ROWS FETCH NEXT 1000 ONLY";
String.format(query, firstrow);
或使用预备陈述
PreparedStatement statement = con.prepareStatement("select * from TABLENAME OFFSET ? ROWS FETCH NEXT 1000 ONLY");
statement.setInt(1, firstrow);
ResultSet rs = statement.executeQuery();
或者您也可以使用此处所述的限制关键字http://docs.oracle.com/javadb/10.10.1.2/ref/rrefjdbclimit.html,您的查询将类似于
String query = "select * from TABLENAME { LIMIT 1000 OFFSET %d }";
String.format(query, firstrow);
答案 1 :(得分:4)
在Oracle中实现分页的常规方法是使用分析窗口函数,例如: row_number
以及定义行排序的ORDER BY
子句。然后将带有分析函数的查询包装到内联视图(或“窗口”)中,您可以从中查询所需的行号。这是一个查询my_table
前1000行(按column_to_sort_by
排序)的示例:
select rs.* from
(select t.*,
row_number() over (order by column_to_sort_by) as row_num
from my_table t
) rs
where rs.row_num >= 1 and rs.row_num < 1001
order by rs.row_num
JDBC实现可能如下所示:
public void queryWithPagination() throws SQLException {
String query = "select rs.* from"
+ " (select t.*,"
+ " row_number() over (order by column_to_sort_by) as row_num"
+ " from my_table t"
+ " ) rs"
+ " where rs.row_num >= ? and rs.row_num < ?"
+ " order by rs.row_num";
final int pageSize = 1000;
int rowIndex = 1;
try (PreparedStatement ps = myConnection.prepareStatement(query)) {
do {
ps.setInt(1, rowIndex);
ps.setInt(2, rowIndex + pageSize);
rowIndex += pageSize;
} while (handleResultSet(ps, pageSize));
}
}
private boolean handleResultSet(PreparedStatement ps, final int pageSize)
throws SQLException {
int rows = 0;
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
/*
* handle rows here
*/
rows++;
}
}
return rows == pageSize;
}
请注意,在您阅读表时,该表应保持不变,以便分页在不同的查询执行中正常工作。
如果表中有这么多行你的内存不足,你可能需要在读完一些页面后清除/序列化你的列表。
修改强>
如果行的排序对你来说无关紧要,那么 - 正如@bdrx在他的回答中提到的那样 - 你可能不需要分页,而最快的解决办法是在没有{的情况下查询表。 {1}}中的{1}}条件。如建议的那样,您可以将语句的提取大小调整为更大的值以提高吞吐量。