4

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

原始表(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;

有什么建议或建议可以帮助我弄清楚吗?谢谢。

4

3 回答 3

3

Merge 语句听起来像一个计划,除了当您执行第一次插入时触发器不会触发,因为您已经提到它是 AFTER UPDATE 触发器,而不是 AFTER INSERT 触发器。

此外,这SELECT pk FROM orig_tab将导致变异表问题

更好的方法是定义一个 AFTER INSERT OR 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; 
于 2012-06-21T10:02:00.283 回答
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;
于 2012-06-22T22:47:57.133 回答
1

这是关于记录是否在 orrig_tabSathya AnswerJaanna更新而在 rep_tab 中没有相应记录的扩展,那么下面的逻辑将满足下面的请求。请不要用这个答案来评判我,因为这个解决方案属于 Sathya

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; 
于 2012-06-21T18:15:03.287 回答