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、複数のユーザーがいる場合は失敗します。実際には、制約を使用します。実際には、何らかの理由でトリガーを使用するように強制された場合、それを適切に実行するには、一連の3つのトリガー、パッケージ、およびコレクションが必要になります。

教授がおそらく探しているもの

ただし、強調するだけで、実際のシステムでこれを行うことを検討することは決してありません。

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 に答える