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;
我尝试做的是:
创建 actual_array
,我们在其中添加 actual_inv_days
作为元素,对于特定年份(yr
)的特定 model
,使用选择月份 pivot
.
一个月一个月,直到达到'pivot month',满足型号和年份。
创建一个 final_array
合并 actual_array
和 forecasted_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构造函数:
我有一个 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;
我尝试做的是:
创建
actual_array
,我们在其中添加actual_inv_days
作为元素,对于特定年份(yr
)的特定model
,使用选择月份pivot
.一个月一个月,直到达到'pivot month',满足型号和年份。
创建一个
final_array
合并actual_array
和forecasted_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构造函数: