web-dev-qa-db-ja.com

Oracle PL / SQLトリガーは、別のテーブルが日付で更新されたときに、1つのテーブルの未処理フィールドを更新します

したがって、例2のテーブルがあります。

movies

  • mid-pk varchar2
  • MovieName-varchar2
  • outstanding-数値

rental

  • sid-数値pk
  • mid-fk varchar2
  • rent_out-日付
  • rent_in-日付

movies.outstandingが入力されたときにrental.rent_inの値を減らすトリガーが必要です。

トリガーでこのようなものを書いてみました

outstanding = outstanding - 1
when mid = old.mid

しかし、トリガーが発火すると、代わりに数を増やして減らします。各行と更新後に使用しました。これで助けが必要です。

レンタルテーブルを更新する最初のトリガーは次のとおりです。

create or replace 
  trigger rentals_rent_trg 
  after insert on rentals 
  for each row 
begin
  update Movies
  set outstanding = outstanding + 1
  where mid = :new.mid;
end;

2番目のトリガーについてサポートが必要です。これは私の2番目のトリガーです。正しくありません。いくつか試しましたが、機能しません。

create or replace 
  trigger rentals_return_trg 
  after update of return_date on rentals 
  for each row 
begin
update Movies
  set outstanding = outstanding - 1
  where :old.mid = :old.mid;
end;

このように2番目のトリガーを書き込もうとすると、ORA-04091を受け取ります。RENTALSが変化し、トリガー/関数がそれを認識しない場合があります。

create or replace 
trigger rentals_return_trg 
after update of return_date on rentals 
for each row 
declare
    return_date_temp date;
begin
    select return_date
    into return_date_temp
    from rentals
    where return_date = :new.return_date
    and return_date is not null;

if return_date_temp is not null
then
      update Movies
     set outstanding = outstanding - 1
     where mid = :new.mid ;
/*mid = :old.mid;*/
end if;
end;

そのエラーが発生したため、compundトリガーを作成しましたが、今は数を減らすことができましたが、代わりに行に基づいています::old.outstanding = 5の場合、1桁下がるだけなので、実際の実行に基づいています:new.outstanding = 4.私は本当に助けが必要です笑。

create or replace trigger rentals_return_trg 
FOR update of return_date on rentals 
COMPOUND TRIGGER
      cursor return_date_cur is
      select R.rid, R.mid, R.return_date, M.outstanding
       from rentals R JOIN MOVIES M
       ON R.mid = M.mid;
       type return_typ is table of return_date_cur%ROWTYPE
       index by binary_integer;
      return_tbl return_typ;
      INT number(4) := 0;
BEFORE STATEMENT IS
begin
for rec in return_date_cur loop
      int := int +1;
      return_tbl(int).rid := rec.rid;
      return_tbl(int).mid := rec.mid;
      return_tbl(int).return_date := rec.return_date;
       return_tbl(int).outstanding := rec.outstanding;
     end loop;
END BEFORE STATEMENT;
AFTER EACH ROW IS
    LV_RETURN_DT DATE;
    LV_OUTSTANDING_NUM NUMBER;
BEGIN
   for i IN 1..return_tbl.count loop
        if return_tbl(i).return_date = :NEW.return_date THEN
        LV_OUTSTANDING_NUM :=  return_tbl(i).outstanding - i;
       exit;
       end if;
  end loop;
  if :new.return_date is not null then
  update movies
  set outstanding = LV_OUTSTANDING_NUM;
  end if;
end after each row;
end;
1
DKCroat

Oracle 12cを使用して、手順をもう一度たどってみました。 2番目のトリガーにはいくつかの変更が必要です...

テスト設定:

create table movies (
  mid varchar2(64) primary key
, MovieName varchar2(64)
, outstanding number
);

create table rentals(
  sid number primary key
, mid varchar2(64) references movies(mid)
, rent_out date
, rent_in date
) ;

-- original trigger -> okay, works for INSERTs 
create or replace 
  trigger rentals_rent_trg 
  after insert on rentals 
  for each row 
begin
  update Movies
  set outstanding = outstanding + 1
  where mid = :new.mid;
end;
/

テストデータ(挿入):

insert into movies (mid, moviename, outstanding)
values (1, 'Snatch', 0);
insert into movies (mid, moviename, outstanding)
values (2, 'Aliens', 0);
insert into movies (mid, moviename, outstanding)
values (3, 'Mars', 0);

-- check
select * from movies;

SQL> select * from movies;
MID  MOVIENAME  OUTSTANDING  
1    Snatch     0            
2    Aliens     0            
3    Mars       0

-- table RENTALS: insert 42 rows for movies 1 and 2
begin
  for i in 1 .. 42 
  loop
    insert into rentals (sid, mid, rent_out, rent_in)
    values (i+100,1,'19-AUG-2017',null); 
    insert into rentals (sid, mid, rent_out, rent_in)
    values (i+200,2,'20-AUG-2017',null); 
  end loop;
end;
/

-- checks
SQL> select count(*) from rentals;
COUNT(*)  
84  

SQL> select * from movies;
MID  MOVIENAME  OUTSTANDING  
1    Snatch     42           
2    Aliens     42           
3    Mars       0  

小さな変更を加えた2番目のトリガーを使用してみましょう。

create or replace 
  trigger rentals_return_trg 
  after update of rent_in on rentals
  for each row 
declare
  new_ number default 0 ;
begin
  new_ := :new.mid ;

  update Movies
  set outstanding = outstanding - 1
  where mid = new_;

end;
/

Trigger RENTALS_RETURN_TRG compiled

ここで、「rentals」テーブルの一部の行を更新し、それぞれ「movies」と「rentals」を確認すると、すべてが期待通りに機能しているように見えます(影響を受ける行のみが選択されます)この例ではレンタルから)。

-- "Aliens" and "Mars" not affected
begin
  for i in 120 .. 125
  loop
    update rentals
    set rent_in = '22-AUG-2017'
    where sid = i ;    
  end loop;
end;
/   

SQL> select * from rentals where rent_in is not null; 
SID  MID  RENT_OUT   RENT_IN    
120  1    19-AUG-17  22-AUG-17  
121  1    19-AUG-17  22-AUG-17  
122  1    19-AUG-17  22-AUG-17  
123  1    19-AUG-17  22-AUG-17  
124  1    19-AUG-17  22-AUG-17  
125  1    19-AUG-17  22-AUG-17 

SQL> select * from movies;
MID  MOVIENAME  OUTSTANDING  
1    Snatch     36           
2    Aliens     42           
3    Mars       0 
1
stefan

これは、トリガーよりも「RentMovie」の手順の方が適しているようです。あまりに頻繁に行うと、少し面倒になる可能性があります。

したがって、「RentMovie」手順では次のようになります。

  • Rental.Rent_Inに入力する
  • Movie.Outstandingの値を1減らします

または、movie.Outstanding updateを独自のプロシージャに分離し、代わりにそのように呼び出すこともできます。

同じ手順で値の増分も処理できることに注意してください(逆のシナリオ)。

1
MguerraTorres