【问题标题】:compound trigger variable initialization复合触发变量初始化
【发布时间】:2019-10-29 11:47:40
【问题描述】:

我有一个如下所示的触发器:

create or replace trigger xxlm_rms_mdm123_aid
  for insert or delete on uda_item_lov
  compound trigger

  type xxlm_rms_mdm123_aid_rec is record(
    item      item_master.item%TYPE,
    uda_value uda_item_lov.uda_value%TYPE);

  type xxlm_rms_mdm123_aid_tbl is table of xxlm_rms_mdm123_aid_rec index by pls_integer;

  L_ctgpro_flag      number;
  L_data_tbl         xxlm_rms_mdm123_aid_tbl;
  L_data_tbl_idx     number;
  L_item             item_master.item%TYPE;
  L_wldpro_uda_id    code_detail.code_desc%TYPE;
  L_ctgpro_uda_id    code_detail.code_desc%TYPE;
  L_wldpro_uda_value uda_item_lov.uda_value%TYPE;

  before statement is
  begin
    L_data_tbl.delete;
    L_data_tbl_idx := 0;
  end before statement;

  before each row is
  begin
    select sign(count(1))
      into L_ctgpro_flag
      from code_detail cd
     where cd.code_type = 'X123'
       and cd.code = 'CTGPRO'
       and cd.code_desc = coalesce(:new.uda_id, :old.uda_id);

    if L_ctgpro_flag = 1 then
      L_data_tbl_idx := L_data_tbl_idx + 1;
      L_data_tbl(L_data_tbl_idx).item := coalesce(:old.item, :new.item);
      L_data_tbl(L_data_tbl_idx).uda_value := coalesce(:old.uda_value,
                                                       :new.uda_value);
    end if;
  end before each row;

  after statement is
  begin
    if L_data_tbl.count > 0 then         
      select max(code_desc)
        into L_wldpro_uda_id
        from code_detail
       where code_type = 'X123'
         and code = 'WLDPRO';

      select max(code_desc)
        into L_ctgpro_uda_id
        from code_detail
       where code_type = 'X123'
         and code = 'CTGPRO';

      if L_wldpro_uda_id is not null then
        for idx in 1 .. L_data_tbl.count loop
          L_item := L_data_tbl(idx).item;

          select max(substr(uv.uda_value_desc, 1, 2))
            into L_wldpro_uda_value
            from uda_values uv
           where uv.uda_id = L_ctgpro_uda_id
             and uv.uda_value = L_data_tbl(idx).uda_value;

          if L_wldpro_uda_value is not null then
            if inserting then
              insert into uda_item_lov
                (item,
                 uda_id,
                 uda_value,
                 create_datetime,
                 last_update_datetime,
                 last_update_id)
              values
                (L_item,
                 L_wldpro_uda_id,
                 L_wldpro_uda_value,
                 sysdate,
                 sysdate,
                 user);
            elsif deleting then
              delete uda_item_lov
               where item = L_item
                 and uda_id = L_wldpro_uda_id
                 and uda_value = L_wldpro_uda_value;
            end if;
          end if;
        end loop;
      end if;
    end if;
  end after statement;
end;

它给出了一个数字错误,即 L_data_tbl_idx 为空。对我来说它看起来很奇怪,因为它应该用 before 语句初始化。 如果我在声明期间初始化变量,就像这样

L_data_tbl         xxlm_rms_mdm123_aid_tbl;
L_data_tbl_idx     number := 0;  -- <-- here it is
L_item             item_master.item%TYPE;

它工作正常。有人可以告诉我这里发生了什么以及为什么在第一种情况下没有初始化变量吗?谢谢!

【问题讨论】:

  • 请问您收到的确切错误信息是什么?我的猜测是 l_data_tbl_idx 不是 null,而是 l_data_tbl(l_data_tbl_idx) 是。
  • 引发“ORA-06502: PL/SQL: numeric or value error: NULL index table key value”

标签: sql oracle plsql triggers


【解决方案1】:

检查复合触发器上的documentation

“复合触发器的可选声明部分声明其所有时间点部分都可以使用的变量和子程序。当触发器触发时,声明部分在任何时间点部分运行之前运行。变量和在触发语句期间存在子程序。”

因此,在您的情况下,它会出错,因为您在时间点部分而不是在声明部分中初始化 l_data_tbl_idxbefore statement 部分中的初始化只存在于该时间点部分的范围内。

【讨论】:

  • 对不起,你的观点对我来说不是很清楚......是的,我在声明部分声明了变量并在 before 语句中为其分配了一个值,我希望稍后在每个行时间点之前看到它.我认为这是复合触发器的目标,使用一种全局变量,其值可以在不同的时间点部分之间共享......我错了吗?为什么你认为“before语句部分的初始化只存在于那个时间点部分的范围内”?
  • 这是您发布的链接中文档的引用:“复合 DML 触发器没有初始化部分,但 BEFORE STATEMENT 部分在任何其他时间点之前运行部分,可以进行任何必要的初始化。”
  • 你好 Kinderproc 我在没有测试的情况下发布了这个,只是通过查看文档。我无法在 db 版本 18.5 上重现这个 - 对我来说,它就像你描述的那样工作。如果您想深入了解它,您可以使用 ddl/dml 发布一个最小的可重现案例吗?我很高兴检查我是否可以复制。
  • 你好科恩!谢谢你的帮助!最后问题出在其他地方,但是您的 cmets 给了我一些想法来检查并找到答案。
【解决方案2】:

我建议这样:

CREATE OR REPLACE TRIGGER xxlm_rms_mdm123_aid
  FOR INSERT OR DELETE ON uda_item_lov
  COMPOUND TRIGGER

  TYPE xxlm_rms_mdm123_aid_rec IS record(
    item      item_master.item%TYPE,
    uda_value uda_item_lov.uda_value%TYPE);

  TYPE xxlm_rms_mdm123_aid_tbl IS TABLE OF xxlm_rms_mdm123_aid_rec; -- without INDEX BY PLS_INTEGER

  L_data_tbl         xxlm_rms_mdm123_aid_tbl;

  BEFORE STATEMENT IS
  BEGIN
    L_data_tbl := xxlm_rms_mdm123_aid_tbl();
  END BEFORE STATEMENT;


  BEFORE EACH ROW IS
  BEGIN
      ...
    L_data_tbl.extend;
    L_data_tbl(L_data_tbl.last).item := COALESCE(:old.item, :new.item);
    L_data_tbl(L_data_tbl.last).uda_value := COALESCE(:old.uda_value, :new.uda_value);

  END BEFORE EACH ROW;

那么您不必维护L_data_tbl_idx 索引。

【讨论】:

  • 是的,这是一个很好的观点,但我的问题更多的是“为什么会发生这种情况?”而不是“如何解决?”。但我会记住的,谢谢!
【解决方案3】:

我想说 before 语句按预期工作。最终发现问题与触发器无关,与插入记录的代码有关。

【讨论】:

    猜你喜欢
    • 2023-04-10
    • 2014-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多