【问题标题】:plsql Trigger to generate and insert valuesplsql 触发器生成和插入值
【发布时间】:2022-01-01 23:47:03
【问题描述】:

我正在尝试创建一个触发器,以便在将插入到另一个表中时自动生成并插入带有年份和月份 (YYYYMM) 的值到表中。

示例: 由于插入到表'original_table'中

create table original_table 
(opt_value char(2),
 low_value varchar2(24),
 high_value varchar2(24));
 
 create table new_values
 (id_values varchar2(24),   
  yr_month number(6));

Insert into original_table(opt_value,low_value,high_value) values ('EQ', '1111111111', '1111111111');
Insert into original_table(opt_value,low_value,high_value) values ('BT', '2222222000', '2222222999');
Insert into original_table(opt_value,low_value,high_value) values ('BT', '3333333350', '3333333399');

原始表

opt_value low_value high_value
EQ 1111111111 1111111111
BT 2222222000 2222222999
BT 3333333350 3333333399

Obs:EQ 代表“相等”,BT 代表“介于”。当'EQ'只需要插入低或高其中一个值时,'BT'需要生成两个值之间的所有数字,然后插入到'new_values'表中。

table 'new_values' 应该得到:

新值

id_values yr_month
1111111111 202111
2222222000 202111
2222222001 202111
2222222002 202111
... ...
2222222999 202111
3333333350 202111
3333333351 202111
... ...
3333333399 202111

我创建了一个可以工作的触发器,但我发现它有点慢,而且我不喜欢使用 BEFORE INSERT 语句,但不能使用 AFTER INSERT 而不会出现变异表错误。

create or replace TRIGGER TRG_NAME
    BEFORE INSERT 
    ON original_table
    FOR EACH ROW
BEGIN

    IF :NEW.opt_value = 'BT' THEN
        INSERT INTO new_values (id_values, yr_month) 
        with tab123 (h_value, l_value, y_month)
                  as (select :NEW.high_value, cast(:NEW.low_value as number) , to_char(trunc(sysdate), 'YYYYMM')
                     from original_table 
                     union all
                     select h_value, l_value +1, y_month
                        from tab123
                       where l_value < h_value)
            select distinct  l_value, y_month
              from tab123;

    ELSIF :NEW.opt_value = 'EQ' THEN
         INSERT INTO new_values (id_values, yr_month) values
             ( :NEW.high_value, to_char(add_months(trunc(sysdate), -1), 'YYYYMM'));    
    END IF;
END;

任何关于如何改进此代码的提示将不胜感激。

【问题讨论】:

  • 该触发器将 所有 行从 original_table(联合其他)插入 new_values(BT 条件)。并且在每一个插入物上。因此,如果您有 10 个值并插入一个新值,它将在 new_values 中插入 11 行(+ 联合结果)。然后,当您添加一个额外的值时,它会插入 12 个值(+ 联合结果)这是预期的行为吗?
  • 不,我已经更改了它,但忘记在我的草稿中更改,在最后一个选择上加上一个不同的。立即编辑。
  • 没有。通常DISTINCT 在这里是一个可怕的解决方案。当您只需要一行时,您没有看到您自己通过读取表中的所有行来创建所有重复项吗?再次删除 DISTINCT 并将 CTE 中的 from original_table 更改为 from dual
  • 当您从正在更改的表中读取数据时,您会收到一个变异表错误。奇怪的是,您在 AFTER INSERT 触发器中而不是在 BEFORE INSERT 触发器中得到它。我会假设它们都会导致相同的错误,因为在这两种情况下,您都是从触发器中的触发表中读取的。好吧,这可能与只插入一行有关。如果您插入更多行,您可能会收到两个触发器变体的错误。而且,如果您按照我的建议删除表格,无论如何您可能会摆脱该错误。
  • 附带说明:为什么将数值作为字符串存储在数据库中?这似乎没有意义。始终使用适当的数据类型将值存储在表中。然后,永远不要使用CHAR;始终使用VARCHAR2CHARVARCHAR2 相比没有优势,只有劣势。

标签: sql oracle recursion plsql


【解决方案1】:

当您从正在更改的表中读取数据时,您会收到一个变异表错误。

奇怪的是,您在 AFTER INSERT 触发器中而不是在 BEFORE INSERT 触发器中获取它。我会假设它们都会导致相同的错误,因为在这两种情况下,您都是从触发器中的触发表中读取的。好吧,这可能与只插入一行有关。如果您一次插入更多行,您可能会收到两种触发器变体的错误。

在您的情况下,从触发器中的 original_table 读取所有行无论如何只会产生重复项,您可以使用DISTINCT 来抵御。相反,不要从表中读取。没有必要。而是从DUAL 读取,以获得具有所需值的一行:

with tab123 (h_value, l_value, y_month) as 
(
  select :new.high_value, cast(:new.low_value as number), to_char(sysdate, 'yyyymm')
  from dual
  union all
  select h_value, l_value + 1, y_month
  from tab123
  where l_value < h_value
)
select l_value, y_month
from tab123;

这将使您摆脱变异表错误并加快触发器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多