PLSQL触发器:更新行时覆盖列

时间:2015-09-04 13:28:39

标签: oracle plsql

我有两个表:1表示我的主要数据(表:ASSET):

ID | DESCR | TYPE1 | COUNTRY
 1 | blabla1 | A1 | AT
 2 | blabla2 | A2 | DE
 3 | blabla3 | A2 | CH

和第二个表(TABLE:ASSET_OVERRULE):

ID | OVERRULE_COLUMN | OVERRULE_VALUE
 1 | TYPE1           | A2

现在我想要一个触发器,每当有人更新表ASSET中的内容时,应该检查表ASSET_OVERRULE。如果找到具有相同ID的条目,则将新更新的值设置为ASSET_OVERRULE中的值。

这是触发器atm:

DECLARE
 CURSOR assets_overrule_cur
  IS
    SELECT *
    FROM asset_overrule
    WHERE id = :NEW.id;
 sTemp VARCHAR2(255):=NULL;
 y NUMBER;
BEGIN
  FOR assets_overrule 
  IN assets_overrule_cur
  LOOP

    sTemp := ':NEW.' || assets_overrule.OVERRULE_COLUMN || ':=''' ||     assets_overrule.OVERRULE_VALUE|| ''';';

    execute immediate sTemp;     

  END LOOP;
END;

但是出现了这个错误:

  

无法找到知识Xpert。

我想,我需要另一种解决方案才能解决这个问题。

2 个答案:

答案 0 :(得分:1)

您不能在动态SQL中使用:new(或:old)伪记录。您需要为每列使用具有单独分支的静态SQL。像

这样的东西
DECLARE
  l_override_val asset_overrule.overrule_value%type;
BEGIN
  BEGIN
    SELECT overrule_value
      INTO l_override_val
      FROM asset_overrule
     WHERE id = :new.id
       AND override_column = 'TYPE1';
    :new.type1 := l_override_val;
  EXCEPTION
    WHEN no_data_found
    THEN
      NULL;
  END;

  BEGIN
    SELECT overrule_value
      INTO l_override_val
      FROM asset_overrule
     WHERE id = :new.id
       AND override_column = 'COUNTRY';
    :new.country := l_override_val;
  EXCEPTION
    WHEN no_data_found
    THEN
      NULL;
  END;

  <<repeat for each column you want to override>>
END;

退一步说,数据模型至少显得笨重。如果要覆盖多个列值而不是让两个表的定义匹配,为什么还有单独的行?您是否真的需要更改触发器中的值而不是简单地在任何视图中执行覆盖返回数据,以便在删除覆盖时返回到系统设置值的任何内容?对于正在执行UPDATE的应用程序来检查覆盖表并且不更新这些值或者警告用户这些值被覆盖或类似的情况,这是否有意义?

答案 1 :(得分:0)

如果我坚持使用这种数据模型,我会怎样做:

drop table test_asset;

drop table test_asset_overrule;

create table test_asset (id number,
                         descr varchar2(10),
                         type1 varchar2(5),
                         country varchar2(3));

create table test_asset_overrule (id number,
                                  overrule_column varchar2(30),
                                  overrule_value varchar2(10));

insert into test_asset
select 1, 'blabla1', 'A1', 'AT' from dual union all
select 2, 'blabla2', 'A2', 'DE' from dual union all
select 3, 'blabla3', 'A2', 'CH' from dual;

insert into test_asset_overrule
select 1, 'TYPE1', 'A2' from dual;

commit;

create or replace trigger test_asset_trg 
before update on test_asset
for each row
begin
  for rec in (select id,
                     overrule_column,
                     overrule_value
              from   test_asset_overrule
              where  id = :old.id
              and    id = :new.id)
  loop
    if upper(rec.overrule_column) = 'TYPE1' then
      :new.type1 := rec.overrule_value;
    elsif upper(rec.overrule_column) = 'COUNTRY' then
      :new.country := rec.overrule_value;
    end if;
  end loop;
end test_asset_trg;
/

update test_asset
set type1 = 'A3';

commit;

select * from test_asset;

        ID DESCR      TYPE1 COUNTRY
---------- ---------- ----- -------
         1 blabla1    A2    AT     
         2 blabla2    A3    DE     
         3 blabla3    A3    CH