1

我刚开始学习触发器,所以请多多包涵。如果插入的行中的礼物与表中已有的任何礼物相同,则打印一条消息,说明该礼物已从捐赠者处赠送给接收者。

create or replace TRIGGER Same_Gift_Given
  BEFORE INSERT ON GIVING
  FOR EACH ROW
DECLARE
  giftgiven varchar(255);
BEGIN
  SELECT giftname INTO giftgiven from GIVING;
  IF :new.giftname = giftgiven then
    dbms_output.put_line(giftgiven || ' has already been gifted to ' || giving.receiver || ' by ' || giving.donor);
  end if;
END;
4

2 回答 2

4

这是一个非常糟糕的家庭作业问题。你永远永远不会触发我们在真实系统中做这样的事情。它会破坏大多数INSERT操作,如果有多个用户,它将失败。实际上,您将使用约束。实际上,如果由于某种原因您被迫在枪口下使用扳机,您将需要一系列三个扳机、一个包和一个集合才能正确执行此操作。

教授可能在寻找什么

只是强调一下,你永远不会考虑在真实系统中这样做

create or replace trigger same_gift_given
  before insert on giving
  for each row
declare
  l_existing_row giving%rowtype;
begin
  select *
    into l_existing_row
    from giving
   where giftname = :new.giftname
     and rownum = 1;

  dbms_output.put_line( :new.giftname || 
                           ' has already been gifted to ' ||
                           l_existing_row.receiver ||
                           ' from ' ||
                           l_existing_row.donor );
exception
  when no_data_found
  then
    null;
end;

这不会阻止您插入重复的行。INSERT ... VALUES如果您尝试在giving桌子上做任何事情,它将引发变异触发器错误。这是低效的。它不处理多个会话。简而言之,这是绝对不应该在任何实际系统中使用的残暴代码。

你在现实中会做什么

实际上,您将创建一个约束

ALTER TABLE giving
  ADD CONSTRAINT unique_gift UNIQUE( giftname );

这将在多用户环境中工作。它不会抛出变异触发器异常。它效率更高。它的代码要少得多。它实际上可以防止插入重复的行。

于 2012-12-13T21:55:38.803 回答
0

让我们尝试一些不同的东西:

CREATE OR REPLACE TRIGGER GIVING_COMPOUND_INSERT
  FOR INSERT ON GIVING
  COMPOUND TRIGGER

  TYPE STRING_COL IS TABLE OF VARCHAR2(255) INDEX BY VARCHAR2(255);

  colGiftnames   STRING_COL;
  aGiftname      VARCHAR2(255);
  nCount         NUMBER;

  -- Note that the way the associative array is used here is a bit of a cheat.
  -- In the BEFORE EACH ROW block I'm putting the string of interest into the
  -- collection as both the value *and* the index.  Then, when iterating the
  -- collection only the index is used - the value is never retrieved (but
  -- since it's the same as the index, who cares?).  I do this because I'd
  -- rather not write code to call a constructor and maintain the collections
  -- size - so I just use an associative array and let Oracle do the work for
  -- me.

  BEFORE EACH ROW IS
  BEGIN
    colGiftnames(:NEW.GIFTNAME) := :NEW.GIFTNAME;
  END BEFORE EACH ROW;

  AFTER STATEMENT IS
  BEGIN
    aGiftname := colGiftnames.FIRST;
    WHILE aGiftname IS NOT NULL LOOP
      SELECT COUNT(*)
        INTO nCount
        FROM GIVING
        WHERE GIFTNAME = aGiftname;

      IF nCount > 1 THEN
        DBMS_OUTPUT.PUT_LINE('Found ' || nCount || ' instances of gift ''' ||
                             aGiftname || '''');
        RAISE_APPLICATION_ERROR(-20001, 'Found ' || nCount ||
                                        ' instances of gift ''' ||
                                        aGiftname || '''');
      END IF;

      aGiftname := colGiftnames.NEXT(aGiftname);
    END LOOP;
  END AFTER STATEMENT;
END GIVING_COMPOUND_INSERT;

同样,这是尝试保证唯一性的糟糕方式。实际上,执行此操作的“正确方法”是使用约束(UNIQUE 或 PRIMARY KEY)。仅仅因为你可以做某事并不意味着你应该做。

分享和享受。

于 2012-12-14T12:56:10.863 回答