1

研究室でコンピュータの使用状況を追跡するためのシステムがあります。少し単純化すると、次のようになります。

  • マシンはラボに関連付けられています。
  • マシンには、ユーザーがログインおよびログアウトすると自動的に更新されるバイナリの logged_in 状態があります。

ラボに関連付けられたシートの総数と、そのラボで現在使用されている数を収集する、ラボをキーとするビューがあります。

私がやりたいのは、時間の経過に伴うラボ人口の変化を追跡する履歴または監査テーブルを追加することです。マシン テーブルが変更されるたびに、ラボ履歴テーブルに時間とラボの総人口を格納するトリガーがマシン テーブルにありました。問題は、ラボの新しい合計を取得するために、マシン テーブルの他の値を調べなければならないことです。これにより、テーブル変更エラーが発生します。

ここや他の場所で見つけたいくつかのことは、変更されたラボを追跡するためのパッケージを作成する必要があることを示唆しています. before トリガーを使用してリストをクリアし、row トリガーを使用して変更された各 labid を保存し、after トリガーを使用して履歴テーブルを更新し、id がリストにあるラボのみの新しい値を使用します。私はそれを試しましたが、パッケージテーブルに保存した値にアクセスする方法がわかりません(または、そもそもそれらを適切に保存している場合でも)。間違いなく明らかなように、私はPL/SQL パッケージとテーブル変数に不慣れです。配列などのテーブル エントリを参照する構文全体が、漠然と異端的であると感じましたが、機能すれば信じられないほど便利です。したがって、以下のほとんどは、私が見つけた他のソリューションからコピーおよび適応しただけですが、そうではありませんでした。そもそも適切に作成されていると仮定して、変更されたラブロシッドのテーブルを実際に使用する方法まで拡張します。以下は、最後のトリガーをコンパイルしようとしたときに pg_machine_in_use_pkg.changedlablocids が存在しないことを単純に示しています。

create or replace package labstats_adm.pg_machine_in_use_pkg
as
  type arr is table of number index by binary_integer;
  changedlablocids arr;
  empty arr;
end;
/

create or replace trigger labstats_adm.pg_machine_in_use_init
  before insert or update 
  on labstats_adm.pg_machine
begin
  -- begin each update with a blank list of changed lablocids
  pg_machine_in_use_pkg.changedlablocids := pg_machine_in_use_pkg.empty;
end;
/

-- 
create or replace trigger labstats_adm.pg_machine_in_use_update
  after insert or update of in_use,lablocid
  on labstats_adm.pg_machine
  for each row
begin
  -- record lablocids - old and new - of changed machines
  if :new.lablocid is not null then
    pg_machine_in_use_pkg.changedlablocids( pg_machine_in_use_pkg.changedlablocids.count+1 ) := :new.lablocid;
  end if;
  if :old.lablocid is not null and :old.lablocid != :new.lablocid then
    pg_machine_in_use_pkg.changedlablocids( pg_machine_in_use_pkg.changedlablocids.count+1 ) := :old.lablocid;
  end if;
end;

create or replace trigger labstats_adm.pg_machine_lab_history
  after insert or update of in_use,lablocid
  on labstats_adm.pg_machine
begin
  -- for each lablocation we just logged a change to, update that labs history
  insert into labstats_adm.pg_lab_history (labid, time, total_seats, used_seats)
    select labid, systimestamp, total_seats, used_seats
      from labstats_adm.lab_usage
      where labid in (
        select distinct labid from pg_machine_in_use_pkg.changedlablocids
        );
end;
/

一方で、パッケージよりも優れた全体的なアプローチがあれば、私はすべて耳にします。

4

2 に答える 2

0

申し訳ありませんが、戻るのが遅くなります。ちょっといじるのに時間がかかりました。それに取り組む時間はあまりありませんでした。

全体の構造を大幅にクリーンアップした複合トリガーを指摘してくれた Bob Jarvis に感謝します。その後、テーブル変数から値を取得する方法をサニタイズする必要がありました。他の誰かが同じ問題への答えを探してこれに出くわすという奇妙な機会に、私はここに私の最終的な解決策を投稿します:

create or replace 
trigger pg_machine_in_use_update
  for insert or update or delete of in_use,lablocid
  on labstats_adm.pg_machine
  compound trigger

  type arr is table of number index by binary_integer;
  changedlabids arr;
  idx binary_integer;

  after each row is
    newlabid labstats_adm.pg_labs.labid%TYPE;
    oldlabid labstats_adm.pg_labs.labid%TYPE;
  begin
    -- store the labids of any changed locations
    -- PL/SQL does not like us testing for the existence of something that isn't there, so just set it twice if necessary
    if ( :new.lablocid is not null ) then
      select labid into newlabid from labstats_adm.pg_lablocation where lablocid = :new.lablocid;
      changedlabids( newlabid ) := 1;
    end if;
    if ( :old.lablocid is not null ) then
      select labid into oldlabid from labstats_adm.pg_lablocation where lablocid = :old.lablocid;
      changedlabids( oldlabid ) := 1;
    end if;
  end after each row;

  after statement is
  begin
    idx := changedlabids.FIRST;
    while idx is not null loop
      insert into labstats_adm.pg_lab_history (labid, time, total_seats, used_seats)
        select labid, systimestamp, total_seats, used_seats
          from labstats_adm.lab_usage
          where labid = idx;
      idx := changedlabids.NEXT(idx);
    end loop;
  end after statement;

end pg_machine_in_use_update;
于 2013-08-09T22:20:07.783 に答える