PostgreSQL generate_series()以SQL函数作为参数

时间:2011-10-31 14:52:32

标签: postgresql datetime plpgsql generate-series

我有一个名为get_forecast_history(integer,integer)的SQL函数,它带有两个参数,一个月和一年。该函数返回使用以下函数创建的CUSTOM TYPE:

CREATE TYPE fcholder AS (y integer, m integer, product varchar, actual real);

函数定义的第一行是:

CREATE OR REPLACE FUNCTION get_forecast_history(integer, integer)
  RETURNS SETOF fcholder AS $$

通话:

SELECT * FROM get_forecast_history(10, 2011);

例如,生成下表(函数的结果类型是一个表,即SETOF):

  y   m product  actual
---- -- -------- ------
2011 10 Product1  29
2011 10 Product2  10
2011 10 Product3  8
2011 10 Product4  0
2011 10 Product5  2

等。 (总共约30种产品)。这是给定月份的历史。

我还有另一个生成一系列月份的查询:

SELECT to_char(DATE '2008-01-01'
            + (interval '1 month' * generate_series(0,57)), 'YYYY-MM-DD') AS ym

哪些产品的列表如下:

ym
----------
2008-01-01
2008-02-01
2008-03-01
2008-04-01
...
2011-10-01

我需要通过获取LEFT JOIN的结果并将它们作为参数传递给函数,以某种方式generate_series上述函数的generate_series年/月组合的结果。这样我就可以获得函数的结果,但是对于generate_series的每年/月组合。此时我被困住了。

我正在使用PostgreSQL 8.3.14。

1 个答案:

答案 0 :(得分:2)

你想要的是这样的:

使用其他信息进行编辑

CREATE OR REPLACE FUNCTION f_products_per_month()
  RETURNS SETOF fcholder AS
$BODY$
DECLARE
    r fcholder;
BEGIN

FOR r.y, r.m IN
    SELECT to_char(x, 'YYYY')::int4  -- AS y
          ,to_char(x, 'MM')::int4    -- AS m
    FROM  (SELECT '2008-01-01 0:0'::timestamp
        + (interval '1 month' * generate_series(0,57)) AS x) x
LOOP
    RETURN QUERY
    SELECT *    -- use '*' in this case to stay in sync
    FROM   get_forecast_history(r.m, r.y);

    IF NOT FOUND THEN
       RETURN NEXT r;
    END IF;
END LOOP;

END;
$BODY$
  LANGUAGE plpgsql;

呼叫:

SELECT * FROM f_products_per_month();

主要观点:

  • 最终编辑,包括一个没有产品的数月的空行。
  • 你写了“LEFT JOIN”,但这不是它的工作原理。
  • 有几种方法可以做到这一点,但RETURN QUERY是最优雅的。
  • 使用与函数get_forecast_history()使用相同的返回类型。
  • 通过表格限定列名称来避免与OUT参数的命名冲突(在最终版本中不再适用)。
  • 不要使用DATE '2008-01-01',像我一样使用时间戳,无论如何都必须转换为to_char()。减少铸造,表现更好(在这种情况下并不重要)。
  • '2008-01-01 0:0'::timestamptimestamp '2008-01-01 0:0'只是两种语法变体。
  • 对于旧版本的PostgreSQL,defualt不会安装语言plpgsql。您可能必须在数据库中发出一次CREATE LANGUAGE plpgsql;。见the manual here

如果需要,您可以将两个函数简化为一个查询或函数。