DO 块工作正常,但函数循环次数太多?

DO block works fine, but function loops too many times?

我有一个 vehicle_data table:

CREATE TABLE public.vehicle_data (
  model_name text NOT NULL,
  record_date text NOT NULL,
  actual_inv_days real,
  forecasted_inv_days real[],
  CONSTRAINT vehicle_data_pkey PRIMARY KEY (model_name, record_date)
)

因此我的 table 看起来像这样:

model_name   record_date  actual_inv_days  forecasted_inv_days
car1          1-2015         33            {22,33,11,22,33,44}
car1          2-2015         22            {25,35,10,22,30}
car1          3-2015         30            {32,30,20,11}

我想为用户选择的月份 n 创建一个合并数组,它显示来自月份 1..n 的 actual_inv_days ,然后是 n 月份的 forecasted_inv_days 在一个数组中。 merged_array('car1',2015,3) 的结果将是:

{33,22,30,32,30,20,11}

下面的函数作为 DO 块工作得很好,但是当我将它修改为一个函数时,它似乎 循环 68 次 超出了必要。 我写的每个函数都会出现这种情况,我不知道该怎么做。

-- DROP FUNCTION public.merged_array(text, integer, integer);
CREATE OR REPLACE FUNCTION public.merged_array(model text, yr integer, pivot integer)
  RETURNS real[] AS
$BODY$
DECLARE actual_arr real[];
    element real;
    final_arr real[];
BEGIN
    FOR i IN 1..pivot 
      LOOP
    element:= (
          SELECT actual_inv_days
          FROM   vehicle_data
          WHERE  model_name = model
          AND split_part(record_date,'-',2)::int = yr
          AND split_part(record_date,'-',1)::int = i);
        -- append new element to existing array
        actual_arr:= actual_arr||element;
    END LOOP;
    final_arr:= actual_arr||(
         SELECT forecasted_inv_days
         FROM   vehicle_data
         WHERE  model_name= model
         AND split_part(record_date,'-',2)::int = yr
         AND split_part(record_date,'-',1)::int = pivot);
    RETURN final_arr;
END$BODY$
  LANGUAGE plpgsql;

我尝试做的是:

  1. 创建 actual_array,我们在其中添加 actual_inv_days 作为元素,对于特定年份(yr)的特定 model,使用选择月份 pivot.

  2. 一个月一个月,直到达到'pivot month',满足型号和年份。

  3. 创建一个 final_array 合并 actual_arrayforecasted_inv_days 数组。

我认为您的代码没有理由 "loops 68 more times than necessary"

尽管如此,就像 a_horse 评论的那样,所有这些都应该是一个查询。您的数据模型可以从重新设计中获益——尤其是列 record_date。模式 1-2015 的字符串是存储该信息的最不受欢迎的方式之一。甚至不允许简单的范围查询。 (2015-01 这样会更有用,但仍然不是最佳选择。)

两个 integer 列会更好。
我会使用一个 date,截断到月份,因为它最适合要存储的信息。占用4个字节。

那么你的 table 可能看起来像这样(其他一切都没有改变):

CREATE TABLE vehicle_data (
  model_name  text NOT NULL
, record_date date NOT NULL
, actual_inv_days real
, forecasted_inv_days real[]
, CONSTRAINT record_date_truncated_to_month CHECK (date_trunc('month', record_date) = record_date)
, CONSTRAINT vehicle_data_pkey PRIMARY KEY (model_name, record_date)
);

然后您可以将查询包装到这个简单的 SQL 函数中,它可以替代您的旧函数:

CREATE OR REPLACE FUNCTION public.merged_array(model text, yr integer, pivot integer)
  RETURNS real[] AS
$func$
   SELECT forecasted_inv_days
       || ARRAY (
            SELECT actual_inv_days
            FROM   vehicle_data
            WHERE  model_name = 
            AND    record_date BETWEEN to_date(yr::text, 'YYYY')
                               AND     to_date(yr||'-'||pivot, 'YYYY-MM')
            ORDER  BY record_date
            )
   FROM   vehicle_data
   WHERE  model_name = model
   AND    record_date = to_date(yr||'-'||pivot, 'YYYY-MM')
$func$  LANGUAGE sql;

当然你现在也可以传递一个实际日期(截断到月份)...

关于to_date() and about date_trunc()的手册。

关于上面相关子查询中使用的ARRAY构造函数: