根据JDBC规范,Statement.setMaxRows(int maxRows)
方法应该是:
设置任何ResultSet的最大行数限制 此Statement对象生成的对象可以包含给定的对象 数。如果超出限制,则多余的行是静默的 丢弃。
在针对限制SQL级别的结果集(ROWSET,TOP和LIMIT)进行测试时,JDBC和SQL构造似乎都表现得非常好。
即使选择了数百万行,setMaxRows
似乎也没有表现得更差。
可能是因为数据库Executor可能使用只按需提取记录的数据库游标,所以当驱动程序达到maxRows
阈值时,可以指示数据库关闭游标吗?
这样,数据库就不必选择一个巨大的结果集并将其发送到线路,只能在客户端丢弃。
答案 0 :(得分:4)
在PostgreSQL中,PgJDBC在协议级别发送一个请求,相当于在查询中附加STKAudioPlayer
。因此,数据库服务器知道尽量减少它所做的工作量。它可能会选择一个获取所有行的成本更高的计划,但是可以更快地返回某些行或避免大的全行排序,例如。
我希望其他引擎的客户端驱动程序类似 - 在幕后设置限制,或使用游标并读取,直到它们有足够的结果。
每个DBMS和驱动程序都会有所不同,因此很难找到一个明确的答案。
答案 1 :(得分:1)
大多数JDBC驱动程序将根据需要获取行(基于获取大小),因此通常maxRows
非常有效。它们通常甚至可以优化,只需获取不超过maxRows
。
ROWS
或TOP
可能会为数据库服务器提供一些优化查询的额外提示,因此设置maxRows
可能不如在查询本身中包含max那样高效。确切的行为取决于驱动程序和数据库,因此很难概括行为和性能特征。
值得注意的例外是MySQL驱动程序(可能还有MariaDB)默认在查询执行时立即获取所有行(除非提取大小设置为Integer.MIN_VALUE
)。
作为Jaybird(Firebird JDBC驱动程序)中的示例,following已完成(TYPE_FORWARD_ONLY
):
public void fetch() throws SQLException {
synchronized (syncProvider.getSynchronizationObject()) {
checkClosed();
int maxRows = 0;
if (this.maxRows != 0) maxRows = this.maxRows - rowNum;
int fetchSize = this.fetchSize;
if (fetchSize == 0) fetchSize = MAX_FETCH_ROWS;
if (maxRows != 0 && fetchSize > maxRows) fetchSize = maxRows;
if (!allRowsFetched && (rows.isEmpty() || rows.size() == rowPosition)) {
rows.clear();
stmt.fetchRows(fetchSize);
rowPosition = 0;
}
if (rows.size() > rowPosition) {
setNextRow(rows.get(rowPosition));
// help the garbage collector
rows.set(rowPosition, null);
rowPosition++;
} else {
setNextRow(null);
}
}
}
由于服务器可能决定发送超过请求的行数,因此会对next()
进行额外检查。
答案 2 :(得分:1)
Oracle使用生产者 - 消费者设计模式。因此,在客户端开始从游标获取到ResultSet时生成行。有两个优化器目标:ALL_ROWS和FIRST_ROWS(分别为FIRST_ROWS(n))。当使用first_rows优化器目标时,Oracle倾向于在hash_joins上使用更多嵌套循环,因此它应该更快地返回第一批结果数据。但我不确定使用setMaxRows方法是否也会更改查询的优化程序目标。