从一组条目计算聚合的 tsrange?

Compute an aggregated tsrange from a set of entries?

我正在尝试根据从 SQL 查询中提取的一组行来计算聚合 tsrange。问题是我不断收到未传入输入参数的错误。

CREATE OR REPLACE AGGREGATE range_merge(anyrange)
(
    sfunc = range_merge,
    stype = anyrange
);

DROP FUNCTION IF EXISTS aggregate_validity(entity_name regclass, entry bigint);

CREATE OR REPLACE FUNCTION aggregate_validity(entity_name regclass, entry bigint) returns tsrange AS
$$
DECLARE
    result tsrange;
BEGIN
   EXECUTE format('select range_merge(valid) from %s where entity_id = %U', entity_name, entry) into result;
   return result;
END
$$ LANGUAGE plpgsql;

当我这样做时:

select * from aggregate_validity(country, 1);

我收到一条错误消息,指出实体名称和条目不存在。它似乎没有正确地将输入参数化到语句中。

函数:

EXECUTE format('select range_merge(valid) from %s where entity_id=%U',entity_name, entry) 
into result;
=>
EXECUTE format('select range_merge(valid) from %I where entity_id=%s',entity_name, entry) 
into result;
--%I for identifier, %s for value

致电:

select * from aggregate_validity(country, 1)
=>
select * from aggregate_validity('country', 1);

db<>fiddle demo

CREATE OR REPLACE AGGREGATE range_merge(anyrange) (
   SFUNC = range_merge
 , STYPE = anyrange
);

-- DROP FUNCTION IF EXISTS aggregate_validity(entity_name regclass, entry bigint);
CREATE OR REPLACE FUNCTION aggregate_validity(entity_name regclass, entry bigint, OUT result tsrange)
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE 'SELECT range_merge(valid) FROM ' || entity_name || ' WHERE entity_id = '
   INTO result
   USING entry;
END
$func$;

致电:

SELECT aggregate_validity('country', 1);

db<>fiddle here

调用不需要 SELECT * FROM,因为函数 returns 每个定义一个值。

我使用了一个 OUT 参数来简化 (OUT result tsrange)。参见:

  • Returning from a function with OUT parameter

不要将 entry 值连接到 SQL 字符串中。使用 USING 子句将其作为 value 传递。更干净,更快。

由于 entity_name 作为 regclass 传递,因此简单地连接是安全的(这会便宜一些)。参见:

  • Table name as a PostgreSQL function parameter

另外,缺少引号和格式说明符不正确,正如 Lukasz 已经提供的那样。

您的自定义聚合函数 range_merge() 有一些 注意事项:

  1. 我不会将它命名为“range_merge”,那也是普通函数 range_merge() 的名称。虽然这是合法的,但它仍然会引起令人困惑的错误。

  2. 您知道函数 range_merge() 在输出范围中包含 输入范围之间的间隙吗?

  3. range_merge() returns NULL 对于任何 NULL 输入。因此,如果您的 table 在列 valid 中有任何 NULL 值,则结果始终为 NULL。我强烈建议任何涉及的列都应定义为 NOT NULL.

如果您可以随意安装其他模块,请考虑 range_agg by Paul Jungwirth who is also here on Stackovflow。它提供了卓越的功能 range_agg() 解决了一些提到的问题。

如果您不想包含空白,请考虑 Postgres Wiki page on range aggregation.

我可能 不会 使用 aggregate_validity()。它掩盖了 Postgres 查询计划器的嵌套功能,并可能导致次优的查询计划。通常,您可以将其替换为 相关或 LATERAL 子查询 ,Postgres 可以在外部查询的上下文中对其进行规划和优化。我在 fiddle:

中附加了一个演示

db<>fiddle here

相关: