从 Java 中的 Postgres 加载非物化数组

Posted

技术标签:

【中文标题】从 Java 中的 Postgres 加载非物化数组【英文标题】:Loading a non-materialised array from Postgres in Java 【发布时间】:2018-09-05 15:51:15 【问题描述】:

设置: 我正在使用 Java 连接到 Postgres 数据库,并尝试加载 pg_stats.histogram_bounds 统计数据,这是 Postgres 中的一个数组。我可以使用Array histogramBounds = rs.getArray("histogram_bounds"); 将该字段提取为sql.Array 对象。这个对象可以用toString()打印,但我不能以其他方式访问任何数据(例如调用histogramBounds.getArray()getBaseType等)都生成PSQLException: No results were returned by the query.

documentation 以及各种教程和 SE 问题表明这应该是有效的。

显然sql.Array对象不直接保存数组数据,而是在服务器上指向它。从我的实验中,我得到了以下 MWE,它显示了存储在 Postgres 表中的数组值与某种短暂或非物化的数组值之间的差异(我怀疑pg_stats 可能是其他内部表的视图)。

MWE: 我有两个查询,一个从表中选择数据并工作,另一个直接从查询中选择数据,但不工作。我在文档中看不出它们不能同时工作的任何原因,我想让第二个版本工作以获取 Java 数组[1, 2, 3]。 令人困惑的是,toString 在第二种情况下仍然可以正确显示数据,所以它必须在某个时候成功加载。

import java.sql.Array;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class PostgresStatsExperiments 
    public static void main(String[] args) throws SQLException 
        Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost/tmp", "admin", "admin");
        Statement stmt = conn.createStatement();

        ResultSet rs = stmt.executeQuery("SELECT name, my_data from array_test;"); // This one works
        //ResultSet rs = stmt.executeQuery("SELECT '1, 2, 3' AS my_data;"); // This one does not work.

        while (rs.next()) 
            Array myData = rs.getArray("my_data");
            System.out.println(myData);
            System.out.println(myData.getArray()); // PSQLException here in the second case
        

        stmt.close();
        rs.close();
        conn.close();
    

表格array_test 如下所示:

create table array_test ( name varchar, my_data integer ARRAY[3] );
insert into array_test values ('Alice', '1, 2, 3');
insert into array_test values ('Bob', '4, 5, 6');

在第二种情况下,我得到以下输出:

1, 2, 3
Exception in thread "main" org.postgresql.util.PSQLException: No results were returned by the query.
    at org.postgresql.jdbc2.TypeInfoCache.getPGArrayElement(TypeInfoCache.java:425)
    at org.postgresql.jdbc2.AbstractJdbc2Array.buildArray(AbstractJdbc2Array.java:540)
    at org.postgresql.jdbc2.AbstractJdbc2Array.getArrayImpl(AbstractJdbc2Array.java:171)
    at org.postgresql.jdbc2.AbstractJdbc2Array.getArray(AbstractJdbc2Array.java:128)
    at tmp.PostgresStatsExperiments.main(PostgresStatsExperiments.java:21)

我用的是Maven的postgresql-9.3-1102-jdbc41.jar,问题也和最新的42.2.4.jre7一样。

【问题讨论】:

【参考方案1】:

在查询SELECT '1, 2, 3' AS my_data 中,my_data不是任何数组。这是一个恰好看起来像数组的字符串 - 但单引号表示text(或varchar)值。

而且由于列的数据类型不是数组,调用getArray()会抛出异常。

如果你想要一个数组,你需要将文本值转换为一个数组:

SELECT '1, 2, 3'::int[] AS my_data;

或更好:使用显式数组构造函数:

SELECT array[1, 2, 3] AS my_data;

pg_stats.histogram_bounds 被定义为anyarray - 它不是类型化数组,因为它包含每列的不同数据类型(这意味着在每一行中,因为该表每个表列包含一行)。

显然 JDBC 驱动程序无法正确使用 anyarray

为了处理来自 Java 代码的值,我认为最简单的方法是将其转换为字符串,然后再转换回文本数组(因为字符串表示是正确的)。

所以如果你使用下面的查询:

select schemaname, tablename, attname, histogram_bounds::text::text[] as histogram_bounds, ..
from pg_stats
where ...

您应该能够检索histogram_bounds 列的内容(但您将所有内容都作为字符串,而不是与该列的数据类型匹配的数据类型)。

【讨论】:

谢谢。 “my_data 不是任何数组。它是一个恰好看起来像数组的字符串” - 现在您指出这一点似乎非常明显;哎呀。也许我被对 rs.getArray() 正常工作的调用所欺骗,这是我不直观地期望的。你知道这是为什么吗? 我曾怀疑anyarray 可能会导致问题,这就是我想首先构建一个更简单的 MWE 作为实验的原因。我很惊讶我没有首先尝试来自pg_data 的单类型数组字段之一,但我想我一定跳过了这一步。如果我将所有内容都转换为字符串,我总是可以从pg_data 中提取 SQL 类型,以帮助在 Java 端进行解码。【参考方案2】:

我也遇到了同样的错误。我的表中有一个 int8[] 类型的列。我使用 DataGrip 程序将我的表移动到另一个模式。当程序将我的表移动到另一个模式时,我看到它将其列类型更改为“文本”。当我更改为 int8[] 类型时,解决了我的问题。

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于从 Java 中的 Postgres 加载非物化数组的主要内容,如果未能解决你的问题,请参考以下文章

在Postgres中的物化视图上创建主键

Postgres中的物化节点之sort节点

Postgres:更新与物化视图连接的表?错误:视图无法在物化视图中锁定行

我们如何检查到目前为止从 Kafka 主题加载到数据库的记录数?

刷新或创建物化视图? Postgres

是否有一个 postgres 命令来列出/删除所有物化视图?