英文:
Cursor/Dynamic SQL to execute Postgres Procedure
问题
Here's the translated portion of your text:
我有一个从表中聚合一些数据并将结果放入不同表格以在PowerBI报告中显示的Postgres存储过程。该存储过程每天聚合数据,因此接受“startDate”和“endDate”参数(其中“endDate”始终为“startDate + 1”)。
CREATE OR REPLACE PROCEDURE schema.DailyCalculation(
    "startDate" timestamp without time zone,
    "endDate" timestamp without time zone)
AS $$
DECLARE
   -- 一些声明
BEGIN
   -- 做一些事情
END;
$$ LANGUAGE plpgsql;
我现在发现了过去计算数据的错误,并希望为日期范围调用此存储过程。为了将其分成可管理的块,我仍然希望为单一日期范围调用“DailyCalculation”,但创建一个新的存储过程,我可以为其提供更长的日期范围,该存储过程将反复调用“DailyCalculation”,只使用日期范围内的单一日期范围。
举个例子,我想重新计算2023年1月。所以,我想调用类似以下的东西:
CALL schema.RecalculateDailyData('2023-01-01', '2023-01-31');
我希望有效执行以下操作:
CALL schema.DailyCalculation('2023-01-01', '2023-01-02');
CALL schema.DailyCalculation('2023-01-02', '2023-01-03');
CALL schema.DailyCalculation('2023-01-03', '2023-01-04');
...
CALL schema.DailyCalculation('2023-01-30', '2023-01-31');
我已经能够生成一个查询,可以生成动态SQL以实现此效果:
SELECT CONCAT('CALL schema.DailyCalculation(''', '', date_trunc('day', dd)::date, '', '', date_trunc('day', dd)::date + 1, '', '');') FROM generate_series( '2023-01-01'::timestamp, '2023-01-31'::timestamp, '1 day'::interval) dd;
如何遍历此结果集并执行动态SQL查询?
英文:
I have a Postgres procedure that aggregates some data from a table and puts the results in a different table for display on a PowerBI report. The procedure aggregates the data on a daily basis, so accepts a startDate and endDate parameter (where endDate is always startDate + 1).
CREATE OR REPLACE PROCEDURE schema.DailyCalculation(
    "startDate" timestamp without time zone,
    "endDate" timestamp without time zone)
AS $$
DECLARE
   -- some declarations 
BEGIN
   -- do some stuff
END;
$$ LANGUAGE plpgsql;
I've now identified an error in the way we calculated data in the past, and want to call this procedure for a date range. To break it into manageable chunks, I still want to call DailyCalculation for a single day period, but create a new procedure that I can provide with a longer date range that will repeatedly call DailyCalculation with only single day ranges in that date range.
For arguments' sake, I want to recalculate January 2023. So, I want to call something like:
CALL schema.RecalculateDailyData('2023-01-01', '2023-01-31');
which I want to effectively do the following:
CALL schema.DailyCalculation('2023-01-01', '2023-01-02');
CALL schema.DailyCalculation('2023-01-02', '2023-01-03');
CALL schema.DailyCalculation('2023-01-03', '2023-01-04');
...
CALL schema.DailyCalculation('2023-01-30', '2023-01-31');
I've been able to generate a query that would generate dynamic SQL to this effect:
SELECT CONCAT('CALL schema.DailyCalculation(''', date_trunc('day', dd)::date, ''', ''', date_trunc('day', dd)::date + 1, ''');') FROM generate_series( '2023-01-01'::timestamp, '2023-01-31'::timestamp, '1 day'::interval) dd;
How do I iterate through this resultset, and execute the dynamic SQL query?
答案1
得分: 1
您可以使用函数 generate_series() 来创建感兴趣的每个日期,并在 for 循环中调用您的存储过程。
create or replace procedure DailyCalculation(
                  startdate  timestamp without time zone 
                , enddate    timestamp without time zone
                )
   language plpgsql
as $$
declare
   -- 一些声明
begin
   insert into demo(routine_name, start_date, end_date)
        values ('Procedure:DailyCalculations', startdate, enddate); 
end;
$$;
-- DO block 来调用 DailyCalculations
do $$
declare 
    sdt record; 
begin
    for sdt in (select dt 
                  from generate_series ('2023-01-01'::timestamp
                                       , '2023-01-31'::timestamp
                                       , interval '1 day' 
                                       ) gs(dt)
               )
    loop 
        call DailyCalculation(sdt.dt, sdt.dt+interval '1 day');
    end loop;
end;
$$; 
或者,您可以创建一个函数(返回 Null),而不是存储过程,并通过单个查询完成此操作。
create or replace function DailyCalculation_fn(
                  startdate  timestamp without time zone 
                , enddate    timestamp without time zone
                )
    returns void    
   language plpgsql 
as $$
declare
   -- 一些声明 
begin
   insert into demo(routine_name, start_date, end_date)
        values ('Function DailyCalculation_fn', startdate, enddate); 
end;
$$; 
select DailyCalculation_fn(dt, dt+interval '1 day') 
  from generate_series ('2023-01-01'::timestamp
                       , '2023-01-31'::timestamp
                       , interval '1 day' 
                       ) gs(dt);
然而,备选函数有时依赖副作用,因此有时被认为是不合适的原因。您可以在这里看到每个示例。对于两者都只是插入到一个示例表中。
注意:您指定了存储过程需要两个参数,startDate 和 endDate,但然后声明结束日期始终是开始日期+1。如果这是必需的,请为什么传递 enddate,只需让您的存储过程/函数计算结束日期。这样,您可以确保结束日期始终正确。当传递 startDate+2 或 startDate+10 或其他不等于 startDate+1 的值时,您的存储过程会执行什么操作?
1: https://dbfiddle.uk/JnWoOyBN
英文:
You can use the function generate_series() to create each date of interested and call your procedure in a for loop.
create or replace procedure DailyCalculation(
                  startdate  timestamp without time zone 
                , enddate    timestamp without time zone
                )
   language plpgsql
as $$
declare
   -- some declarations 
begin
   insert into demo(routine_name, start_date, end_date)
        values ('Procedure:DailyCalculations', startdate,enddate); 
end;
$$;
-- DO block to call DailyCalculations
do $$
declare 
    sdt record; 
begin
    for sdt in (select dt 
                  from generate_series ( '2023-01-01'::timestamp
                                       , '2023-01-31'::timestamp
                                       , interval '1 day' 
                                       ) gs(dt)
               )
    loop 
        call DailyCalculation(sdt.dt, sdt.dt+interval '1 day');
    end loop;
end;
$$; 
Alternatively you can create a Function (returns Null) instead of a Procedure and accomplish this with a single query.
create or replace function DailyCalculation_fn(
                  startdate  timestamp without time zone 
                , enddate    timestamp without time zone
                )
    returns void    
   language plpgsql 
as $$
declare
   -- some declarations 
begin
   insert into demo(routine_name, start_date, end_date)
        values ('Function DailyCalculation_fn', startdate,enddate); 
end;
$$; 
select DailyCalculation_fn(dt, dt+interval '1 day') 
  from generate_series ('2023-01-01'::timestamp
                       , '2023-01-31'::timestamp
                       , interval '1 day' 
                       ) gs(dt);
The alternative function however does rely side effects and is sometimes considered inappropriate for that reason.
See demo for each here. For demo both just insert into a demo table.
NOTE: You specified tour procedure needs 2 parameters, startDate and endDate, but then declare that end date is always be start+1. If this is required, why pass enddate just let your procedure/function calculate the end date. That way you ensure the end date is always correct. What does your Procedure do when startDate+2 or startDate+10 or anything other than startdate+1 is passed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论