plpgsql 专家:(记录集)函数的输入和输出

Posted

技术标签:

【中文标题】plpgsql 专家:(记录集)函数的输入和输出【英文标题】:plpgsql expert : (set of record) input and output for a function 【发布时间】:2013-09-18 13:39:06 【问题描述】:

我需要一些关于 postgres 中 plpgsql 函数的帮助。 这个问题已在 psql-general 上提出,但没有确定的答案(here 和 here)

归结为:

如何创建一个 plpgsql 函数,其中一组行作为输入,一组行作为输出不使用数组(为了提高性能)。

我目前将输入作为光标的引用传输,但它并不令人满意,因为它会强制计算两次相同的查询 (SELECT ...hard_work...) 并且它会在我的应用程序中弄乱事务。

目前它的工作方式如下:

DECLARE cursor FOR ...hardwork...;

WITH first_query AS (
SELECT ...hard_work... --second computation of hard_work
   ),
second_query AS ( 
    SELECT ...another_query_using_hard_work...
   )
SELECT *
FROM my_function('cursor'::refcursor) f(...) ;

最终我想拥有类似(不工作)的东西

WITH first_query AS ( 
    SELECT ...hard_work...
   ),
second_query AS ( 
    SELECT ...another_query_using_the_hard_work_query...
   )
SELECT *
FROM my_function('first_query'::regclass) f(...) ;

当然 SELECT ...hard_work... 很昂贵(大约 50 毫秒),最好不要计算两次。 应用是数据流,所以时间很宝贵,数据量很大,所以在临时表中复制数据可能比计算两次(通常是几十MB)更糟糕。

提出了其他解决方案

传输视图或表引用作为输入,这与使用游标的问题相同(即:计算的两倍,单独的语句)

传输/输出数组:使用 array_agg() 和 unnest() 强制进行大量计算

传输临时表:涉及复制数据:可能比计算两次要长(我正在争取 50 毫秒)

传输物化视图:仅在 9.3 中可用

如果能对这个主题有深入的了解,我将非常感激。

最好的问候,

雷米-C

PS : psql-general mailing list 上的问题链接包含很多关于特定甚至所有代码的详细信息。

PPS:版本:postgres 9.2。操作系统:Ubuntu 12.04 LTE,客户端:PGAdmin3 用于测试,node.js 用于生产。

【问题讨论】:

结果集/表不是 PostgreSQL 中的第一类对象。通常这是通过将结果传递到具有硬编码名称的临时表中来解决的。 您已经发布了 3 次相同的问题,但实际上并未提供所涉及查询的详细信息。你所能期待的只是模糊的概括。 您好@Richard Huxtons 感谢您的回答,我知道没有所有细节很难回答,但请注意我不是在寻求帮助来优化查询,而只是一种不计算它的一般方法两次。该查询是关于 12Go 100 万行表上的空间和时间过滤,具有索引和所有内容。我的项目是关于在 postgres (github here) 中存储大量点云。同样,这个查询大约是 50 毫秒。 您可能必须在内部使用数组,但您可以使用聚合或窗口函数来执行此操作。你不能只传递一个集合,它必须逐行传递。但是对于聚合,你可以使用任何你想要的内部格式。 【参考方案1】:

据我所知,您还没有尝试使用temporary tables。

您可以使用 硬编码 名称,甚至使用 动态 名称,可能使用 SEQUENCE 来获得唯一的表名称。然后,您将在 PL/pgSQL 函数中使用带有 EXECUTE 的动态 SQL,并将表名(或者更好的是:对象标识符类型 regclass)传递给它。

请务必在较大的临时表上手动运行ANALYZE 在重大更改之后(通常是在最初填充它之后),因为临时表对 autovacuum 守护进程不可见。 甚至可能在大型临时表上创建索引!

您可以在 *** 上找到许多代码示例。一个特别丰富和松散相关的答案:Refactor a PL/pgSQL function to return the output of various SELECT queries

【讨论】:

感谢您的回答(也有@Quassnoi)。我编辑了这个问题,以便更准确地了解这个解决方案。使用临时表会涉及数据复制,我猜这会超过 50 毫秒。 (编辑:尝试一下:至少是 100 毫秒) @Remi-C:“数据复制”在这种情况下是指复制到 RAM。如果您想对数据做任何事情,这一步似乎是不可避免的...... 是的,你是对的,我希望一切都在 RAM(大型服务器)中,但创建这个临时表仍然需要 100 毫秒。也许来自 WAL 日志记录的开销等等?干杯,Rémi-C @Remi-C:临时表没有 WAL 日志记录。但是该表已在目录表中注册,这意味着在创建时和会话结束时进行微小的写入操作。【参考方案2】:

我认为解决此类问题的典型方法是使用自定义聚合/函数。然后你可以使用任何你想要的内部存储。每一行都直接传入,转换,然后再处理。

【讨论】:

以上是关于plpgsql 专家:(记录集)函数的输入和输出的主要内容,如果未能解决你的问题,请参考以下文章

泛型集返回函数

Postgresql - 从 plpgsql 函数返回记录 []

如何将表行传递给 plpgsql 函数?

如何在 VB.NET 中将 '<unnamed portal 1>' 转换为 plpgsql 函数的数据集

检查 plpgsql 中的当前日志记录级别

在 plpgsql 中,如何从返回记录的函数中退出