PL/SQL 遍历 table 类型并扩展

PL/SQL loop over table type and extend

我目前正在学习 PL/SQL,我的任务是创建一个包,用于查找所有德国假期并将它们插入一个集合中。我的问题是我必须通过遍历它们并扩展 table 来插入它们。手动扩展 table 很容易,但工作量太大,我的老师不允许手动扩展。我感谢任何解决方案或帮助。

    CREATE OR REPLACE PACKAGE BODY pa_feiertage_mau IS


  FUNCTION calc_holidays (in_year_c IN INTEGER)
    RETURN DATE
  IS
     v_k     INTEGER;
     v_m     INTEGER;
     v_s     INTEGER;
     v_a     INTEGER;
     v_d     INTEGER;
     v_r     INTEGER;
     v_og    INTEGER;
     v_sz    INTEGER;
     v_oe    INTEGER;
     v_os    INTEGER;
     v_day   INTEGER;
     v_month INTEGER;
  BEGIN
   v_k := floor(in_year_c / 100);
   v_m := 15 + floor((3 * v_k + 3) / 4) - floor((8 * v_k + 13) / 25);
   v_s := 2 - floor((3 * v_k + 3) / 4);
   v_a := MOD(in_year_c, 19);
   v_d := MOD((19 * v_a + v_m), 30);
   v_r := floor(v_d / 29) + (floor(v_d / 28) - floor(v_d / 29)) * floor(v_a / 11);
   v_og := 21 + v_d - v_r;
   v_sz := 7 - MOD((in_year_c + floor(in_year_c / 4) + v_s), 7);
   v_oe := 7 - MOD(v_og - v_sz, 7);
   v_os := v_og + v_oe;
   IF (v_os <= 31) THEN
    v_day := v_os;
    v_month := 3;
   ELSE
    v_day := v_os - 31;
    v_month := 4;
   END IF;
  RETURN TO_DATE(v_day || '.' || v_month || '.' || in_year_c, 'DD,MM,YYYY');

  END calc_holidays;


  FUNCTION get_holidays (in_year1 IN VARCHAR2)
    RETURN Type_tab_feiertage
    PIPELINED
  IS
    /*Static holidays */

    v_year1   VARCHAR2(4)   := in_year1;
    v_neujahr VARCHAR2(10)  := '01.01.';
    v_3kings  VARCHAR2(10)  := '06.01.';
    v_fraut   VARCHAR2(10)  := '08.03.';
    v_arbeit  VARCHAR2(10)  := '01.05.';
    v_himmel  VARCHAR2(10)  := '15.08.';
    v_deu     VARCHAR2(10)  := '03.10.';
    v_refo    VARCHAR2(10)  := '31.10.';
    v_aller   VARCHAR2(10)  := '01.11.';
    v_wh1     VARCHAR2(10)  := '25.12.';
    v_wh2     VARCHAR2(10)  := '26.12.';

    /*Changing holidays*/

    d_ostern   DATE         := calc_holidays(TO_NUMBER(in_year1));
    d_karf     DATE         := d_ostern   - INTERVAL '2'  DAY;
    d_gruen    DATE         := d_ostern   - INTERVAL '3'  DAY;
    d_osterm   DATE         := d_ostern   + INTERVAL '1'  DAY;
    d_chimmel  DATE         := d_ostern   + INTERVAL '39' DAY;
    d_pfingsts DATE         := d_ostern   + INTERVAL '49' DAY;
    d_pfingstm DATE         := d_pfingsts + INTERVAL '1'  DAY;
    d_fronlei  DATE         := d_ostern   + INTERVAL '60' DAY;
    
    tab_feiertage     type_tab_feiertage;
    
  BEGIN
    
    tab_feiertage   := NEW type_tab_feiertage();

    LOOP

      tab_feiertage.EXTEND();
      tab_feiertage( tab_feiertage.LAST).DATUM    := TO_DATE( v_neujahr || v_year1, 'DD.MM.YYYY');
      tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Neujahr';
      

    END LOOP;

    IF tab_feiertage.COUNT > 0 THEN
      FOR i IN tab_feiertage.FIRST .. tab_feiertage.LAST
      LOOP
        PIPE ROW( tab_feiertage( i));
      END LOOP;
      --RETURN tab_feiertage;
    END IF;

    RETURN;

  END get_holidays;  
END pa_feiertage_mau;

老实说,很难说出你的老师在说什么。您有一些固定的引用数据键(节假日名称)和派生它们的引用值的逻辑,您需要将键和派生值放在一个集合中。没有什么神奇的方法可以做到这一点:每个条目都需要手动分配。

我个人会丢弃局部变量(v_%d_%)并将该逻辑和名称放入集合填充代码中。我要保留的唯一变量是 d_ostern,因为您需要它来锚定所有其他可移动的盛宴。请注意 Pfingstm 派生方式的变化。

d_ostern := calc_holidays(TO_NUMBER(in_year1));

tab_feiertage.EXTEND();
tab_feiertage( tab_feiertage.LAST).DATUM    := TO_DATE( '01.01.' || v_year1, 'DD.MM.YYYY');
tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Neujahr';
...
tab_feiertage.EXTEND();
tab_feiertage( tab_feiertage.LAST).DATUM    := d_ostern;
tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Ostern';
tab_feiertage.EXTEND();
tab_feiertage( tab_feiertage.LAST).DATUM    := d_ostern - INTERVAL '2'  DAY;
tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Karf';
...
tab_feiertage.EXTEND();
tab_feiertage( tab_feiertage.LAST).DATUM    := d_ostern + INTERVAL '50'  DAY;
tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Pfingstm');
...

有一种方法可以不使用手动扩展和显式分配来填充集合,但它需要 SQL。在这里,我们使用 WITH 子句调用 calc_holidays() 函数来计算 Ostern 的值,然后编写一个大量的 UNION ALL 查询,该查询选择 key-value 对并使用 BULK COLLECT:[=17 将它们存储在集合中=]

select datum, feiertag
bulk collect into tab_feiertage
from (
    /* calculate moveable feast */
    with hol as ( 
      select calc_holidays(TO_NUMBER(in_year1)) as ostern
      from   dual ) 

    /* Static holidays */
    select to_date('01.01.'||in_year1, 'DD.MM.YYYY') as datum, 'Neujahr'   as feiertag from hol union all
    select to_date('06.01.'||in_year1, 'DD.MM.YYYY') as datum, '3kings'    as feiertag from hol union all
    select to_date('08.03.'||in_year1, 'DD.MM.YYYY') as datum, 'Fraut'     as feiertag from hol union all
    select to_date('01.05.'||in_year1, 'DD.MM.YYYY') as datum, 'Arbeit'    as feiertag from hol union all
    select to_date('15.08.'||in_year1, 'DD.MM.YYYY') as datum, 'Himmel'    as feiertag from hol union all
    select to_date('03.10.'||in_year1, 'DD.MM.YYYY') as datum, 'Deu'       as feiertag from hol union all
    select to_date('31.10.'||in_year1, 'DD.MM.YYYY') as datum, 'Refo'      as feiertag from hol union all
    select to_date('01.11.'||in_year1, 'DD.MM.YYYY') as datum, 'Aller'     as feiertag from hol union all
    select to_date('25.12.'||in_year1, 'DD.MM.YYYY') as datum, 'Wh1'       as feiertag from hol union all
    select to_date('26.12.'||in_year1, 'DD.MM.YYYY') as datum, 'Wh2'       as feiertag from hol union all
    
    /* Changing holidays */
    select ostern                                             , 'Ostern'   as feiertag from hol union all
    select ostern - INTERVAL  '2' DAY as datum                , 'Karf'     as feiertag from hol union all
    select ostern - INTERVAL  '3' DAY as datum                , 'Gruen'    as feiertag from hol union all
    select ostern + INTERVAL  '1' DAY as datum                , 'Osterm'   as feiertag from hol union all
    select ostern + INTERVAL '39' DAY as datum                , 'Chimmel'  as feiertag from hol union all
    select ostern + INTERVAL '49' DAY as datum                , 'Pfingsts' as feiertag from hol union all
    select ostern + INTERVAL '50' DAY as datum                , 'Pfingstm' as feiertag from hol union all
    select ostern + INTERVAL '60' DAY as datum                , 'Fronlei'  as feiertag from hol
);

这可能不是您的老师所期望的,但它确实有效。 db<>fiddle here

上有演示