PLSQL触发器更新另一个表中的字段值

时间:2012-06-21 09:43:14

标签: oracle plsql oracle11g plsqldeveloper

我对触发器很陌生,所以显然我在某处做错了。我正在处理一个报告表,它将从原始表中获取数据。为简单起见,假设有一个表,然后有一个报告表。

原始表格(orig_tab)

CREATE TABLE orig_tab (
PK     NUMBER(8)       not null,
NAME   VARCHAR2(20)            ,
);

INSERT INTO orig_tab (PK, NAME) VALUES (1, 'AAA');
INSERT INTO orig_tab (PK, NAME) VALUES (2, 'BBB');
INSERT INTO orig_tab (PK, NAME) VALUES (3, 'CCC');

然后是报告表(rep_tab)

CREATE TABLE rep_tab (
PK     NUMBER(8)       not null,
NAME   VARCHAR2(20)            ,
);

现在,从用户界面,有人更改记录2的值。显然,这应该被视为报告表的插入(因为此记录不存在)。然后在一段时间后,值会更改,因此它是报告表的更新案例。

问题:我怎么能做出这种触发?我认为这是一个合并的州议案例。

这就是我所做的:

create or replace trigger vr_reporting_trigger
after update on orig_tab
  for each row
begin
  MERGE INTO rep_tab d
  USING (SELECT pk FROM orig_tab) s
  ON (d.pk = s.pk)
  WHEN MATCHED THEN
  UPDATE SET d.pk = s.pk,
             d.name = s.name
  WHEN NOT MATCHED THEN
  INSERT (d.pk, d.name) VALUES (s.pk, s.name);
end vr_reporting_trigger;

任何可以帮我弄清楚的建议或建议?感谢。

3 个答案:

答案 0 :(得分:3)

合并语句听起来像一个计划,除了你在第一次插入时触发器不会触发,因为你提到它是一个AFTER UPDATE触发器,而不是AFTER INSERT触发器。

此外,SELECT pk FROM orig_tab会产生Mutating table problem

更好的方法是定义一个AFTER INSERT或UPDATE触发器,将它与INSERT / UPDATING关键字结合起来处理插入/更新&使用:new / :old来处理新数据&旧数据分别。

CREATE OR replace TRIGGER vr_reporting_trigger
  AFTER INSERT OR UPDATE ON orig_tab
  FOR EACH ROW
BEGIN
    IF inserting THEN
      INSERT INTO rep_tab
                  (pk,
                   name)
      VALUES      (:NEW.pk,
                   :NEW.name);
    ELSIF updating THEN
      UPDATE rep_tab r
      SET    name = :NEW.name
      WHERE  r.pk = :old.pk;
    END IF;
END vr_reporting_trigger; 

答案 1 :(得分:3)

在以前的答案中,有些角落案件没有处理。

如果在插入行时报告表中已存在匹配的pk,该怎么办? (我们通常不希望这种情况发生,但考虑如果有人从orig_tab中删除了一行,然后再次插入它会发生什么。(这就是那种将要出现的问题。生产,而不是在测试中,在最不合适的时间。现在更好地计划。)

BEGIN
   IF inserting THEN
      -- insure we avoid duplicate key exception with a NOT EXISTS predicate
      INSERT INTO rep_tab(pk,name)
      SELECT :new.pk, :new.name FROM DUAL
      WHERE NOT EXISTS (SELECT 1 FROM rep_tab WHERE pk = :new.pk);
      -- if row already existed, there's a possibility that name does not match
      UPDATE rep_tab t SET t.name = :new.name 
       WHERE t.pk = :new.pk;
      -- could improve efficiency of update by checking if update is actually
      -- needed using a nullsafe comparison ( t.name <=> :new.name );
   ELSIF updating THEN
      -- handle updates to pk value (note: the row to be updated may not exist
      -- so we need to fallthru to the merge)
      IF :new.pk <> :old.pk THEN
         UPDATE rep_tab t
            SET t.pk = :new.pk
              , t.name = :new.name
          WHERE t.pk = :old.pk ;
      END IF;
      MERGE INTO rep_tab d
      USING DUAL ON (d.pk = :old.pk)
      WHEN MATCHED THEN
      UPDATE SET d.name = :new.name
      WHEN NOT MATCHED THEN
      INSERT (d.pk,d.name) VALUES (:new.pk,:new.name);
   END IF;
END;

答案 2 :(得分:1)

这是Sathya Answer Jaanna的扩展,因为CREATE OR replace TRIGGER vr_reporting_trigger AFTER INSERT OR UPDATE ON orig_tab FOR EACH ROW BEGIN IF inserting THEN INSERT INTO rep_tab (pk, name) VALUES (:NEW.pk, :NEW.name); ELSIF updating THEN MERGE INTO rep_tab d USING DUAL ON (d.pk =:OLD.pk) WHEN MATCHED THEN UPDATE SET d.name = :OLD.name WHEN NOT MATCHED THEN INSERT (d.pk,d.name) VALUES (:OLD.PK,:NEW.PK ); END IF; END vr_reporting_trigger; 询问记录是否在orrig_tab中更新而在rep_tab中没有相应的记录,然后下面的逻辑将满足下面的请求。请不要评判我这个解决方案属于Sathya

{{1}}