Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何读取大表中的所有行?_Java_Postgresql_Jdbc - Fatal编程技术网

Java 如何读取大表中的所有行?

Java 如何读取大表中的所有行?,java,postgresql,jdbc,Java,Postgresql,Jdbc,我在处理数据库(PostgreSQL)中的所有行时遇到问题。我得到一个错误:org.postgresql.util.psqleexception:检索查询结果时内存不足。我想我需要一小段地读取所有行,但它不起作用-它只读取100行(下面的代码)。怎么做 int i = 0; Statement s = connection.createStatement(); s.setMaxRows(100); // bacause of: org.postgresql.u

我在处理数据库(PostgreSQL)中的所有行时遇到问题。我得到一个错误:
org.postgresql.util.psqleexception:检索查询结果时内存不足。
我想我需要一小段地读取所有行,但它不起作用-它只读取100行(下面的代码)。怎么做

    int i = 0;      
    Statement s = connection.createStatement();
    s.setMaxRows(100); // bacause of: org.postgresql.util.PSQLException: Ran out of memory retrieving query results.
    ResultSet rs = s.executeQuery("select * from " + tabName);      
    for (;;) {
        while (rs.next()) {
            i++;
            // do something...
        }
        if ((s.getMoreResults() == false) && (s.getUpdateCount() == -1)) {
            break;
        }           
    }

我认为你的问题类似于这个主题:它包含了满足你需求的解决方案

特别是对于PostgreSQL,您可以在请求中使用LIMIT和OFFSET关键字:

PS:在Java代码中,我建议您使用PreparedStatement而不是简单的语句:

使用or


处理大型数据集时,“限制”和“偏移”会变慢。

我像下面这样做。我认为这不是最好的方式,但它确实有效:)


短版本为call
stmt.setFetchSize(50)
连接设置自动提交(假)
以避免将整个
结果集
读入内存

下面是他们所说的:

基于光标获取结果

默认情况下,驱动程序一次收集查询的所有结果。 这对于大型数据集来说可能不方便,因此JDBC驱动程序 提供一种将结果集基于数据库游标且仅基于数据库游标的方法 正在获取少量行

少量行缓存在连接的客户端 当耗尽时,下一个行块由 重新定位光标

注:

  • 基于光标的结果集不能在所有情况下使用。有很多限制会让司机安静下来 退回到一次获取整个结果集

  • 与服务器的连接必须使用V3协议。这是服务器版本的默认设置(仅受服务器版本支持) 7.4及更高版本-

  • 连接不能处于自动提交模式。后端在事务结束时关闭游标,因此处于自动提交模式 后端将关闭光标,然后才能执行任何操作 从中提取-

  • 必须使用ResultSet.type\u FORWARD\u的ResultSet类型创建语句。这是默认值,因此不会出现任何代码 需要重写以利用这一点,但它也 意味着您不能向后滚动或以其他方式跳转 在结果集中-

  • 给定的查询必须是单个语句,而不是用分号串在一起的多个语句

示例5.2。设置提取大小以打开和关闭光标。

将代码更改为游标模式非常简单,只需将语句的获取大小设置为适当的大小。将提取大小设置回0将导致缓存所有行(默认行为)



至少在我的例子中,问题出在试图获取结果的客户端上

想要得到一个包含所有结果的.csv文件

我通过使用

psql -U postgres -d dbname  -c "COPY (SELECT * FROM T) TO STDOUT WITH DELIMITER ','"

(其中dbname是数据库的名称…)并重定向到文件。

因此,问题的关键在于,默认情况下,Postgres以“自动提交”模式启动,并且它需要/使用游标才能“分页”数据(例如:读取前10K结果,然后读取下一个,然后读取下一个),但是,游标只能存在于事务中。因此,默认情况下,总是将所有行读取到RAM中,然后允许您的程序在所有行都到达后开始处理“第一个结果行,然后是第二个结果行”,原因有两个,它不在事务中(因此游标不工作),而且还没有设置提取大小

因此,
psql
命令行工具如何实现查询的批处理响应(其
FETCH\u COUNT
设置),就是在短期事务中“包装”其select查询(如果事务尚未打开),以便游标可以工作。您也可以使用JDBC执行类似操作:

  static void readLargeQueryInChunksJdbcWay(Connection conn, String originalQuery, int fetchCount, ConsumerWithException<ResultSet, SQLException> consumer) throws SQLException {
    boolean originalAutoCommit = conn.getAutoCommit();
    if (originalAutoCommit) {
      conn.setAutoCommit(false); // start temp transaction
    }
    try (Statement statement = conn.createStatement()) {
      statement.setFetchSize(fetchCount);
      ResultSet rs = statement.executeQuery(originalQuery);
      while (rs.next()) {
        consumer.accept(rs); // or just do you work here
      }
    } finally {
      if (originalAutoCommit) {
        conn.setAutoCommit(true); // reset it, also ends (commits) temp transaction
      }
    }
  }
  @FunctionalInterface
  public interface ConsumerWithException<T, E extends Exception> {
    void accept(T t) throws E;
  }
static void readLargeQueryInChunksJdbcWay(连接连接、字符串原始查询、int fetchCount、ConsumerWithException consumer)抛出SQLException{
布尔值originalAutoCommit=conn.getAutoCommit();
如果(原始自动提交){
conn.setAutoCommit(false);//启动临时事务
}
try(Statement=conn.createStatement()){
语句。setFetchSize(fetchCount);
ResultSet rs=语句.executeQuery(原始查询);
while(rs.next()){
消费者。接受(rs);//还是你在这里工作
}
}最后{
如果(原始自动提交){
conn.setAutoCommit(true);//重置它,还将结束(提交)临时事务
}
}
}
@功能接口
公共接口使用者WithException{
无效接受(T)抛出E;
}
这样做的好处是需要更少的RAM,而且,在我的结果中,似乎总体运行速度更快,即使您不需要保存RAM。奇怪的它还提供了一个好处,即您对第一行的处理“开始得更快”(因为它一次处理一个页面)

下面介绍了如何使用“原始postgres游标”方式,以及完整的演示,尽管在我的实验中,上面的JDBC方式似乎由于任何原因稍微快了一点


另一个选项是在任何地方关闭
自动提交
模式,尽管您仍然必须为每个新语句手动指定一个fetchSize(或者您可以在URL字符串中设置一个默认的fetchSize)。

只需使用Spring,实际上不需要针对JDK类编写代码-对于非常大的结果集,LIMIT和OFFSET的伸缩性不好:\n试试这个:这有什么缺点吗?我是否应该在所有查询中启用它(从文档中的措辞来看,它在所有情况下听起来都很好;如果您阅读的是大表,则更好;如果您阅读的是小表,则无所谓)
psql -U postgres -d dbname  -c "COPY (SELECT * FROM T) TO STDOUT WITH DELIMITER ','"
  static void readLargeQueryInChunksJdbcWay(Connection conn, String originalQuery, int fetchCount, ConsumerWithException<ResultSet, SQLException> consumer) throws SQLException {
    boolean originalAutoCommit = conn.getAutoCommit();
    if (originalAutoCommit) {
      conn.setAutoCommit(false); // start temp transaction
    }
    try (Statement statement = conn.createStatement()) {
      statement.setFetchSize(fetchCount);
      ResultSet rs = statement.executeQuery(originalQuery);
      while (rs.next()) {
        consumer.accept(rs); // or just do you work here
      }
    } finally {
      if (originalAutoCommit) {
        conn.setAutoCommit(true); // reset it, also ends (commits) temp transaction
      }
    }
  }
  @FunctionalInterface
  public interface ConsumerWithException<T, E extends Exception> {
    void accept(T t) throws E;
  }