PL SQL 通过日期范围迭代循环

时间:2021-06-16 08:13:13

标签: oracle plsql

我有一个日期范围,我试图通过循环每周约会一次

DECLARE
  start_date DATE := TO_DATE('06.01.2021', 'dd.MM.yyyy');
  end_date DATE := TO_DATE('26.05.2021', 'dd.mm.yyyy');
  active_date DATE;
  start_number NUMBER;
  end_number NUMBER;
BEGIN
  start_number := TO_NUMBER(TO_CHAR(start_date, 'j'));
  end_number := TO_NUMBER(TO_CHAR(end_date, 'j'));
  active_date := start_date;
FOR cur_r IN start_number..end_number
  LOOP
   INSERT INTO test_tbl
   SELECT snap_date FROM s_act
   WHERE
    snap_date = active_date;
    active_date := TRUNC(active_date) + 7;
COMMIT;
END LOOP;
END;

当我执行此脚本时,只有一个日期 06.01.2021 通过所有迭代写入表。 我在哪里犯了错误?如何解决这个问题?

2 个答案:

答案 0 :(得分:2)

为此您不需要 PL/SQL,只需使用递归子查询即可:

INSERT INTO test_tbl
WITH date_range ( start_date, end_date ) AS (
  SELECT DATE '2021-01-06', DATE '2021-05-26' FROM DUAL
UNION ALL
  SELECT start_date + INTERVAL '7' DAY,
         end_date
  FROM   date_range
  WHERE  start_date + INTERVAL '7' DAY <= end_date
)
SELECT snap_date
FROM   s_act s
WHERE  EXISTS(
         SELECT 1
         FROM   date_range r
         WHERE  r.start_date = s.snap_date
       );

或分层查询:

INSERT INTO test_tbl
SELECT snap_date
FROM   s_act s
WHERE  EXISTS(
         WITH date_range ( start_date, end_date ) AS (
           SELECT DATE '2021-01-06', DATE '2021-05-26' FROM DUAL
         )
         SELECT 1
         FROM   date_range r
         WHERE  r.start_date + ( LEVEL - 1 ) * INTERVAL '7' DAY = s.snap_date
         CONNECT BY r.start_date + ( LEVEL - 1 ) * INTERVAL '7' DAY <= r.end_date
       );

如果您真的想使用 PL/SQL,那么您可以让它更简单,并按周而不是几天进行迭代(但是,这会降低效率,因为您每周将有一个 INSERT 以及相关的与 SQL 解决方案相比,从 PL/SQL 到 SQL 的上下文切换,整个操作只有一个 INSERT,没有上下文切换):

DECLARE
  start_date  DATE := DATE '2021-01-06';
  end_date    DATE := DATE '2021-05-26';
  active_date DATE := start_date;
BEGIN
  LOOP
    EXIT WHEN active_date > end_date;

    INSERT INTO test_tbl
    SELECT snap_date FROM s_act
    WHERE  snap_date = active_date;

    active_date := active_date + INTERVAL '7' DAY;
  END LOOP;
END;
/

db<>fiddle here

答案 1 :(得分:1)

对我来说,看起来好像一切都好,实际上,你编写的代码没问题,因为 active_date 获得了它的新值:

SQL> set serveroutput on;
SQL> declare
  2    start_date   date := to_date('06.01.2021', 'dd.MM.yyyy');
  3    end_date     date := to_date('26.05.2021', 'dd.mm.yyyy');
  4    active_date  date;
  5    start_number number;
  6    end_number   number;
  7  begin
  8    start_number := to_number(to_char(start_date, 'j'));
  9    end_number   := to_number(to_char(end_date, 'j'));
 10    active_date  := start_date;
 11
 12    for cur_r in start_number..end_number
 13    loop
 14      dbms_output.put_line('Active_date = ' || to_char(active_date, 'dd.mm.yyyy'));
 15      /* Commented, as I don't have your tables nor data
 16         INSERT INTO test_tbl
 17           SELECT snap_date
 18           FROM s_act
 19           WHERE snap_date = active_date;
 20      */
 21      active_date := trunc(active_date) + 7;
 22    end loop;
 23    -- move COMMIT out of the loop!
 24    commit;
 25  end;
 26  /
Active_date = 06.01.2021
Active_date = 13.01.2021
Active_date = 20.01.2021
<snip>
Active_date = 06.09.2023
Active_date = 13.09.2023

PL/SQL procedure successfully completed.

SQL>

你说

<块引用>

当我执行此脚本时,只有一个日期 06.01.2021 通过所有迭代写入表中。

这是一段代码:

INSERT INTO test_tbl
  SELECT snap_date 
  FROM s_act
  WHERE snap_date = active_date;

我解释为:

  • s_act 表包含行,其中 snap_date 等于 06.01.2021,或

  • 如果它包含具有其他日期的行,则它们可能包含时间组件(小时、分钟、秒)并且 where 条件阻止它们被插入。如果是这样,请尝试

    where trunc(snap_date) = active_date
    

    看看会发生什么。