JDBC:LIMIT vs setmaxrows(Resultset)

时间:2015-09-22 03:03:03

标签: java postgresql jdbc

我想选择前10个记录,我可以使用以下其中一个:

  • 在Resultset中使用setmaxrows
  • 在sql语句中使用LIMIT和OFFSET

我的问题是:它们之间的优点和缺点是什么?

5 个答案:

答案 0 :(得分:12)

SQL级限制

要限制SQL查询结果集的大小,可以使用SQL:008语法:

SELECT title
FROM post
ORDER BY created_on DESC
OFFSET 50 ROWS
FETCH NEXT 50 ROWS ONLY

可在Oracle 12,SQL Server 2012或PostgreSQL 8.4或更高版本上使用。

对于MySQL,您可以使用LIMIT和OFFSET子句:

SELECT title
FROM post
ORDER BY created_on DESC
LIMIT 50
OFFSET 50

使用SQL级分页的优点是数据库执行计划可以使用此信息。

因此,如果我们在created_on列上有一个索引:

CREATE INDEX idx_post_created_on ON post (created_on DESC)

我们执行以下使用LIMIT子句的查询:

EXPLAIN ANALYZE
SELECT title
FROM post
ORDER BY created_on DESC
LIMIT 50

我们可以看到数据库引擎使用了索引,因为优化器知道只提取50条记录:

Execution plan:
Limit  (cost=0.28..25.35 rows=50 width=564)
       (actual time=0.038..0.051 rows=50 loops=1)
  ->  Index Scan using idx_post_created_on on post p  
      (cost=0.28..260.04 rows=518 width=564) 
      (actual time=0.037..0.049 rows=50 loops=1)
Planning time: 1.511 ms
Execution time: 0.148 ms

JDBC语句maxRows

根据setMaxRows Javadoc

如果超过了限制,多余的行将被静默删除。

那不是很让人放心!

因此,如果我们在PostgreSQL上执行以下查询:

try (PreparedStatement statement = connection
    .prepareStatement("""
        SELECT title
        FROM post
        ORDER BY created_on DESC
    """)
) {
    statement.setMaxRows(50);
    ResultSet resultSet = statement.executeQuery();
    int count = 0;
    while (resultSet.next()) {
        String title = resultSet.getString(1);
        count++;
    }
}

我们在PostgreSQL日志中获得以下执行计划:

Execution plan:
  Sort  (cost=65.53..66.83 rows=518 width=564) 
        (actual time=4.339..5.473 rows=5000 loops=1)
  Sort Key: created_on DESC
  Sort Method: quicksort  Memory: 896kB
  ->  Seq Scan on post p  (cost=0.00..42.18 rows=518 width=564) 
                          (actual time=0.041..1.833 rows=5000 loops=1)
Planning time: 1.840 ms
Execution time: 6.611 ms 

由于数据库优化器不知道我们只需要获取50条记录,因此它假定所有5000行都需要扫描。如果查询需要获取大量记录,则全表扫描的成本实际上比使用索引的成本低,因此执行计划将完全不使用索引。

我在Oracle,SQL Server,PostgreSQL和MySQL上进行了此测试,看起来Oracle和PostgreSQL优化器在生成执行计划时未使用maxRows设置。

但是,在SQL Server和MySQL上,考虑了maxRows JDBC设置,执行计划等效于使用TOPLIMIT的SQL查询。您可以自己运行测试,因为这些测试在我的High-Performance Java Persistence GitHub repository中可用。

结论

尽管看起来setMaxRows是可移植的解决方案来限制ResultSet的大小,但是如果数据库服务器优化程序不使用JDBC { {1}}属性。

有关如何使用Oracle,SQL Server,PostgreSQL和MySQL编写Top-N SQL查询的更多详细信息,请查看this article

答案 1 :(得分:4)

setmaxrows 的优点是你可以创建通用语句,在Postgres,Oracle,Mysql等中有效 由于Oracle使用rownum语法,postgres - limit,msqsql - top

速度似乎没什么区别。

答案 2 :(得分:4)

对于大多数情况,您希望使用LIMIT子句,但在一天结束时,两者都将达到您想要的效果。这个答案针对JDBC和PostgreSQL,但适用于使用类似模型的其他语言和数据库。

Statement.setMaxRows的JDBC文档说

  

如果超出限制,则会以静默方式删除多余的行。

即。数据库服务器可能返回更多行,但客户端将忽略它们。 PostgreSQL JDBC驱动程序限制客户端和服务器端。对于客户端,请查看maxRows in the AbstractJdbc2ResultSet的用法。对于服务器端,请查看maxRows in QueryExecutorImpl

服务器端,PostgreSQL LIMIT documentation说:

  

查询优化器在生成查询时会考虑LIMIT   计划

因此,只要查询合理,它就只会加载完成查询所需的数据。

答案 3 :(得分:2)

setFetchSize 为JDBC驱动程序提供有关当此Statement生成的ResultSet对象需要更多行时应从数据库中提取的行数的提示。

setMaxRows 设置此Statement对象生成的任何ResultSet对象可以包含给定数字的最大行数限制。

我想使用上面的2个JDBC API,您可以尝试使用setFetchSize,如果它适用于100K记录,您可以尝试。否则,您可以批量获取并形成ArrayList并将其返回到Jasper报告中。

答案 4 :(得分:1)

不确定我是否正确,但我记得在过去我参与了一个大型项目,将所有预期会退回一行的查询更改为“TOP 1'或numrows = 1。原因是数据库会停止搜索下一个可能的匹配项。当这个提示'被使用了。在高容量环境中,这确实有所不同。你可以忽略的评论'客户端或结果集中的多余记录是不够的。你应该尽早避免不必要的阅读。但我不知道JDBC方法是否将这些特定于数据库的提示添加到查询y / n。我可能需要测试才能看到并使用它...我不是数据库专家,可以想象我不对,但是" Speedwise似乎没什么区别"可能是一个错误的假设...例如如果你被要求在盒子里搜索红球并且你只需要一个,那么它就不会增加价值来继续搜索所有你在哪里就足够了......那么重要的是要指定' TOP 1' ......