在不执行查询的情况下获取 Postgres 查询结果的类型

Posted

技术标签:

【中文标题】在不执行查询的情况下获取 Postgres 查询结果的类型【英文标题】:Get types of Postgres query result without executing query 【发布时间】:2021-01-15 09:11:30 【问题描述】:

给定一个正在运行的 Postgres 集群,我如何获取任意查询结果的列的类型名称数组?我宁愿避免执行查询,因为查询可能已准备好,这意味着如果不为准备好的表达式指定具体值,我将无法运行查询。

示例架构

CREATE TYPE book_type AS ENUM ('FICTION', 'NONFICTION');

CREATE TABLE books (
  book_id   SERIAL PRIMARY KEY,
  book_type book_type NOT NULL DEFAULT 'FICTION'
);

如何获取以下查询的返回行的列的类型名称?

SELECT book_id,
       book_type,
       'arbitrary_column',
       '2021-01-14'::date - INTERVAL '3 hour'
FROM books
WHERE book_id = $1;

上述查询的预期类型名称是:

['int', 'varchar', 'varchar', 'timestamp'] 

这可以通过 SQL 实现吗?如果没有,是否有扩展或 C 函数?我知道执行查询时会返回类型,但我想知道是否可以在不执行查询的情况下获取类型。

相关

https://dba.stackexchange.com/questions/75015/query-to-output-names-and-data-types-of-a-query-table-or-view - 通过使用查询创建临时表然后自省表来提供答案。聪明而且有效,但需要执行查询。

【问题讨论】:

SQL Server 有sp_describe_first_result_set。我不知道 Postgres 是否有类似的东西。 我认为你可以通过 JDBC 做到这一点 如果 JDBC 支持在不执行查询的情况下获取结果类型,我会感到惊讶。 JDBC 最终必须运行 SQL 才能与 Postgres 交互。 【参考方案1】:

这里有 3 个有效的步骤,但它确实需要执行查询。在我的情况下这没问题,但一般不会回答这个问题。

    准备查询,可选择添加LIMIT 0WHERE false 子句以确保查询不执行任何操作。

    PREPARE sample_query AS
      SELECT book_id,
            book_type,
            'arbitrary_column'                     AS third,
            '2021-01-14'::date - INTERVAL '3 hour' AS fourth
      FROM books
      WHERE book_id = $1
      LIMIT 0;
    

    通过执行准备好的查询创建一个临时表,所有参数都使用null

    CREATE TEMP TABLE tmp_sample 
      AS EXECUTE sample_query ( NULL );
    

    自省临时表以获取原始查询的返回类型。

    SELECT attname, format_type(atttypid, atttypmod) AS type
    FROM pg_attribute
    WHERE attrelid = 'tmp_sample'::regclass
      AND attnum > 0
      AND NOT attisdropped
    ORDER BY attnum;
    
    +---------+---------------------------+
    |attname  |type                       |
    +---------+---------------------------+
    |book_id  |integer                    |
    |book_type|book_type                  |
    |third    |text                       |
    |fourth   |timestamp without time zone|
    +---------+---------------------------+
    

一个可能的问题是所有列名都必须是唯一的。查询可以包含重复的列名,但表不能。这意味着您只能拥有 1 个未命名的列,因为未命名的列始终使用名称 ?column?

我不执行查询的最初原因是因为我不想弄清楚如何为每个准备好的参数类型生成表达式。由于null 适用于所有类型,我们可以无条件地为每个准备好的参数使用它。

【讨论】:

这是作弊 - 你正在执行查询。但我认为在 SQL 中没有不同的方法可以做到这一点。一些客户端 API 有方法来描述语句的结果集。 是的,绝对是作弊;我会在答案中更清楚地说明这一点。对于我的特定用例来说这没问题的原因是因为我可以对准备好的语句中的任何表达式使用 null。如果在 prod 数据库上运行,这种方法有潜在的危险,但我的计划是在空的测试数据库上运行这些。我需要支持类似于 sqlc 的代码生成的类型。

以上是关于在不执行查询的情况下获取 Postgres 查询结果的类型的主要内容,如果未能解决你的问题,请参考以下文章

在不实际执行查询的情况下确定 Oracle 查询执行时间和建议的数据大小

在不使用事务的情况下从多个查询中获取行 ID

如何在不创建函数的情况下运行 plpgsql?

如何在不使用字段名的情况下运行 django orm 查询?

ElasticSearch使用 _validate API 在不执行查询的情况下验证查询

在不循环的情况下查询 Firestore 集合中的所有文档