我正在将我们的主db从Server迁移到PostgreSQL (在这个过程中学习它)。
我需要移动的东西之一是一堆存储过程,它们生成具有动态列名的表。我的工作没有任何问题。问题是,其中一些表后来被用来获取数据,以便在另一个存储过程中生成另一个表。这些后续程序可能需要很长时间才能生成,有时需要3-5分钟。虽然这些查询相当复杂,但我怀疑这是因为动态创建的表不包含索引,甚至不包含主键。
下面是最不复杂过程的示例代码。有人能告诉我,在这个过程中,我如何在第一列(material
)中添加一个主键,然后向第六列(total_open
)添加额外的索引?
CREATE OR REPLACE PROCEDURE sap_data.sp_ssa_po_final()
LANGUAGE plpgsql
AS $procedure$
DECLARE
month_next_5 varchar(3) := to_char(NOW() + interval '5 month', 'mon');
month_next_4 varchar(3) := to_char(NOW() + interval '4 month', 'mon');
month_next_3 varchar(3) := to_char(NOW() + interval '3 month', 'mon');
month_next_2 varchar(3) := to_char(NOW() + interval '2 month', 'mon');
month_next_1 varchar(3) := to_char(NOW() + interval '1 month', 'mon');
month_now varchar(3) := to_char(NOW(), 'mon');
month_prev_1 varchar(3) := to_char(NOW() - interval '1 month', 'mon');
month_prev_2 varchar(3) := to_char(NOW() - interval '2 month', 'mon');
month_prev_3 varchar(3) := to_char(NOW() - interval '3 month', 'mon');
month_prev_4 varchar(3) := to_char(NOW() - interval '4 month', 'mon');
month_prev_5 varchar(3) := to_char(NOW() - interval '5 month', 'mon');
month_prev_6 varchar(3) := to_char(NOW() - interval '6 month', 'mon');
sql_string varchar(3000) := '';
BEGIN
sql_string := 'CREATE TABLE sap_ssa_po_final AS SELECT
mat_no AS "material",
material_descr AS "material_description",
('||month_now||'+'||month_prev_1||'+'||month_prev_2||'+'||month_prev_3||'+'||month_prev_4||'+'||month_prev_5||'+'||month_prev_6||') as "open_till_'||month_now||'",
'||month_next_1||' AS "sum_'||month_next_1||'_qty",
'||month_next_2||' AS "sum_'||month_next_2||'_qty",
('||month_now||'+'||month_prev_1||'+'||month_prev_2||'+'||month_prev_3||'+'||month_prev_4||'+'||month_prev_5||'+'||month_prev_6||'+'||month_next_1||'+'||month_next_2||') as "total_open",
'||month_next_3||' AS "sum_'||month_next_3||'_qty",
'||month_next_4||' AS "sum_'||month_next_4||'_qty",
'||month_next_5||' AS "sum_'||month_next_5||'_qty",
NOW() as created_date
FROM v_ssa_po_summarised';
execute sql_string;
end;
$procedure$
;
我确实尝试过在谷歌上寻找答案,但结果却很短。:(
发布于 2022-02-18 17:43:00
我是被你代码中的噪音触发的。考虑一下重写。
同时,我也回答了你的问题。
CREATE OR REPLACE PROCEDURE sap_data.sp_ssa_po_final()
LANGUAGE plpgsql AS
$proc$
DECLARE
mon text[];
sql_string text;
BEGIN
-- prepare array with subscripts -6 to +5, and current month at index 0
SELECT INTO mon
('[-6:5]={' || string_agg(to_char(m, 'mon'), ',') || '}')::text[]
FROM generate_series(LOCALTIMESTAMP - interval '6 mon'
, LOCALTIMESTAMP + interval '5 mon'
, interval '1 mon') m;
sql_string :=
'CREATE TABLE public.sap_ssa_po_final AS SELECT'
|| concat_ws(E'\n , '
, E'\n mat_no AS material'
, 'material_descr AS material_description'
, '(' || concat_ws('+', mon[0], mon[-1], mon[-2], mon[-3], mon[-4], mon[-5], mon[-6]) || ') AS open_till_' || mon[0]
, mon[1] || ' AS sum_' || mon[1] || '_qty'
, mon[2] || ' AS sum_' || mon[2] || '_qty'
, '(' || concat_ws('+', mon[0], mon[-1], mon[-2], mon[-3], mon[-4], mon[-5], mon[-6], mon[1], mon[2]) || ') AS total_open'
, mon[3] || ' AS sum_' || mon[3] || '_qty'
, mon[4] || ' AS sum_' || mon[4] || '_qty'
, mon[5] || ' AS sum_' || mon[5] || '_qty'
, 'now() AS created_date'
)
|| E'\nFROM v_ssa_po_summarised;'
-- RAISE NOTICE '%', mon;
-- RAISE NOTICE '%', sql_string;
EXECUTE sql_string;
-- These can be static. Spell out the schema to be sure!
ALTER TABLE public.sap_ssa_po_final ADD PRIMARY KEY(material);
CREATE INDEX ON public.sap_ssa_po_final (total_open);
END
$proc$;
在创建新表之后,可以使用静态代码创建主键和索引。PL/pgSQL计划并执行一个又一个语句。(这在普通SQL函数中是不可能的,因为整个函数体都是一次解析的。但是动态SQL无论如何都需要一种过程语言。)请参见:
但是,我建议将模式名称拼写出来,以避免与search_path
发生意外(甚至是恶意的尝试)。我用的是public
。(您的动态CREATE TABLE
语句目前还没有模式。)适应你的情况。也许临时模式pg_temp
是您的一种选择?请参见:
我生成月份mon
的数组,当前月份的下标为0。这样可以简化相当多的内容。关于非标准数组下标:
注意concat_ws()
的战略使用。请参见:
我使用LOCALTIMESTAMP
而不是now()
来清楚地表明,任何一种方式都将使用本地时区--这对拐角处的情况有很大的影响。在generate_series()
中,它的效率也略高一些。请参见:
https://dba.stackexchange.com/questions/307683
复制