Блок DO работает нормально, но функция зацикливается слишком много раз? - PullRequest
1 голос
/ 25 марта 2019

У меня есть таблица vehicle_data:

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)
)

Следовательно, моя таблица выглядит следующим образом:

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 , который показывает actual_inv_days из месяцев 1..n , а затем forecasted_inv_days месяца n в одном массиве.Результат 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 в качестве элементов для определенного model для определенного года (yr), с выбранным месяцем как pivot.

  2. Идет месяц за месяцем, пока не будет достигнут «сводный месяц», когда модель и год удовлетворены.

  3. Создайте final_array, который объединяет actual_array и forecasted_inv_days массив.

1 Ответ

1 голос
/ 25 марта 2019

Я не вижу причины в вашем коде для "зацикливается на 68 раз больше, чем необходимо" .

Как бы то ни было, как прокомментировал a_horse, все это должно быть одним запросом. И ваша модель данных может извлечь выгоду из редизайна - особенно столбец record_date. Строка шаблона 1-2015 является одним из наименее благоприятных способов хранения этой информации. Даже не допускает простых запросов диапазона. (2015-01 было бы более полезным, но все же неоптимальным.)

Два integer столбца будут лучше.
Я бы использовал один date, усеченный до месяца, так как он лучше всего подходит для сохраняемой информации. Занимает 4 байта.

Тогда ваш стол мог бы выглядеть так (все остальное без изменений):

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 = $1
            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() и о date_trunc().

О конструкторе ARRAY, используемом в коррелированном подзапросе выше:

...