0

したがって、JOBS と TASKS という 2 つのテーブルがあります。

TASKS テーブルには、状態の進行 (「送信中」、「送信済み」、「承認済み」、「完了済み」など) を格納する TAKS_STATUS 列があります。

JOBS テーブルには、TASKS テーブルの TASK_STATUS 列のロールアップ (つまり、最小状態) である JOB_STATUS 列があります。JOBS テーブルには、ジョブに関連付けられた TASKS の数を含む TASK_COUNT 値もあります。

ジョブには 1 つ以上のタスクを含めることができます。各テーブルの JOB_ID はそれらをリンクします。

DB2 には、この状態をロールアップする一連の単純なトリガーがあります。これが真ん中の1つです:

create or replace trigger JOB_AUTHORED
  after update of TASK_STATUS on TASKS
  referencing NEW as N
  for each row
  when (TASK_STATUS = 'Authored')
    update JOBS
      set JOB_STATUS = 'Authored'
      where JOBS.JOB_ID = N.JOB_ID
        and TASK_COUNT=(
          select count(0) from TASKS
            where TASKS.JOB_ID = N.JOB_ID
              and TASKS.TASK_STATUS in ('Authored','Completed'))

トリガーはトリガー イベントと同じワーク ユニットで実行され、ワー​​ク ユニットのコミットされていない変更を確認でき、行ロックにヒットすることなく発生したばかりの TASK_STATUS 変更をカウントできるため、これは DB2で正常に機能します。

Oracle の翻訳されたトリガーは次のとおりです。

create or replace trigger JOB_AUTHORED
  after update of TASK_STATUS on TASKS
  for each row
  when (NEW.TASK_STATUS = 'Authored')
  BEGIN
    update JOBS
      set JOB_STATUS='Authored'
      where JOBS.JOB_ID = :NEW.JOB_ID and TASK_COUNT=(
        select count(0) from TASKS
          where TASKS.JOB_ID = :NEW.JOB_ID
            and TASKS.TASK_STATUS in ('Authored','Completed'));
  END;

Oracle では、これは次のように失敗します。

ORA-04091: table MYSCHEMA.TASKS is mutating, trigger/function may not see it#012ORA-06512: at "MYSCHEMA.JOB_AUTHORED", line 1#012ORA-04088: error during execution of trigger 'MYSCHEMA.JOB_AUTHORED'#012] [query: UPDATE TASKS SET TASK_STATUS=:1 where TASK_ID=:2

どうやらOracleのトリガーは同じコンテキストで実行されず、コミットされていないトリガー更新を確認できないため、トリガー行を含む特定の状態のタスクの数を数えることはできません。

AFTER トリガーを INSTEAD OF トリガーに変更し、トリガー内で TASK_STATUS (および JOB_STATUS) を更新できると思います (ジョブ更新でタスク更新を確認できるようにするため) が、同じエラーが発生しますか? 最初のタスクの更新ではないかもしれませんが、トリガー プログラムがコミット前に多数の TASKS を更新している場合はどうでしょうか。2 番目のタスクが更新されるとどうなりますか?

また、トリガーを削除して、プログラムでアクティブなジョブをスキャンしてそのタスクのステータスを確認することも検討しましたが、それは洗練されていないようです。

Oracleでこのようなものを使用したベストプラクティスは何ですか?

4

3 に答える 3

2

ベスト プラクティスは、可能であればトリガーを避けることです。
トリガーを使用すべきではない理由については、次のリンクを参照してください
http://www.oracle.com/technetwork/testcontent/o58asktom-101055.html
are-evil.html

トリガーの代わりにプロシージャ (API) を使用します。 、などadd_new_jobのいくつかのプロシージャを含むパッケージを作成し、これらのプロシージャにすべてのロジックを配置できます (チェック、タスクの状態の変更、ジョブの変更)。州など)一箇所に。理解しやすく、保守しやすく、デバッグしやすく、エラーを追跡しやすい。add_new_taskchange_task_status


トリガーの使用を主張する場合はcompound trigger、Tom Kyte が上記の最初のリンクで言及したように、 を作成できますworkaround。たとえば、次のようになります。

create or replace TRIGGER JOB_AUTHORED
FOR UPDATE OF TASK_STATUS on TASKS
COMPOUND TRIGGER

  TYPE JOB_ID_TABLE_TYPE IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;  
  JOB_ID_TABLE JOB_ID_TABLE_TYPE;
  dummy CHAR;

    AFTER EACH ROW IS
     BEGIN
       -- SELECT null INTO dummy FROM JOBS WHERE job_id = :NEW.JOB_ID FOR UPDATE;
        JOB_ID_TABLE( :NEW.JOB_ID ) := :NEW.JOB_ID;
     END AFTER EACH ROW;

     AFTER STATEMENT IS
     BEGIN
       FORALL x IN INDICES OF JOB_ID_TABLE
         UPDATE jobs set JOB_STATUS='Authored'
         WHERE JOBS.JOB_ID = JOB_ID_TABLE( x )
           and TASK_COUNT=(
                  select count(0) from TASKS
                  where TASKS.JOB_ID = JOBS.JOB_ID
                    and TASKS.TASK_STATUS in ('Authored','Completed')
          );
     END AFTER STATEMENT;

END JOB_AUTHORED;

しかし.....
この例に落とし穴がないかどうかはわかりません。現時点では認識していません。

たとえば、このシナリオには 1 つの落とし穴が あります。
ステータスを持つ 18 のタスクがあるとします。Authored

  • X ユーザー A が UPDATE TASK SET status ' Authored' WHERE task_id = 2 を実行すると、トリガーが起動され、18+1 個のコミットされたタスクがステータスと共に表示されます。Authored
  • X+10ms の時点で、ユーザー B は UPDATE TASK1 SET status ' Authored' task_id = 4 を実行します。Authored
  • X+20ms の時点でユーザー A がコミット
  • X+30ms の時点でユーザー B がコミット
  • 最後に、 status を持つ 21 個のタスクがありますauthored。ただし、ジョブのステータスは変更されていません Authored(ただしAuthored、タスク数 = 20 の場合は変更する必要があります)。

このトラップを回避するには、トリガーSELECT null INTO dummy FROM JOBS WHERE job_id = :NEW.JOB_ID FOR UPDATE;の一部で使用して、after each rowアクセスをシリアル化するために JOBS テーブルの対応するレコードにロックを配置できます (上記の例ではコメントされています)。
しかし、これが正しい解決策であるかどうかはまだわかりません。現時点では想像も予測もできないシナリオでデッドロックが発生する可能性があります。

于 2015-05-25T23:49:34.790 に答える
0

ベスト プラクティスは、可能であればトリガーを避けることです。
トリガーを使用すべきでない理由については、次のリンクを参照してください:
http://www.oracle.com/technetwork/testcontent/o58asktom-101055.html
are-evil.html

トリガーの代わりにプロシージャ (API) を使用します。 、などadd_new_jobのいくつかのプロシージャを含むパッケージを作成し、これらのプロシージャにすべてのロジックを配置できます (チェック、タスクの状態の変更、ジョブの変更)。州など)一箇所に。理解しやすく、保守しやすく、デバッグしやすく、エラーを追跡しやすい。add_new_taskchange_task_status


トリガーの使用を主張する場合はcompound trigger、Tom Kyte が上記の最初のリンクで言及したように、 を作成できますworkaround。たとえば、次のようになります。

create or replace TRIGGER JOB_AUTHORED
FOR UPDATE OF TASK_STATUS on TASKS
COMPOUND TRIGGER

  TYPE JOB_ID_TABLE_TYPE IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;  
  JOB_ID_TABLE JOB_ID_TABLE_TYPE;
  dummy CHAR;

    AFTER EACH ROW IS
     BEGIN
       -- SELECT null INTO dummy FROM JOBS WHERE job_id = :NEW.JOB_ID FOR UPDATE;
        JOB_ID_TABLE( :NEW.JOB_ID ) := :NEW.JOB_ID;
     END AFTER EACH ROW;

     AFTER STATEMENT IS
     BEGIN
       FORALL x IN INDICES OF JOB_ID_TABLE
         UPDATE jobs set JOB_STATUS='Authored'
         WHERE JOBS.JOB_ID = JOB_ID_TABLE( x )
           and TASK_COUNT=(
                  select count(0) from TASKS
                  where TASKS.JOB_ID = JOBS.JOB_ID
                    and TASKS.TASK_STATUS in ('Authored','Completed')
          );
     END AFTER STATEMENT;

END JOB_AUTHORED;

しかし.....
この例に落とし穴がないかどうかはわかりません。現時点では認識していません。

たとえば、このシナリオには 1 つの落とし穴が
あります。18 個のタスクがステータス付きであるとしますAuthored

  • X ユーザー A が UPDATE TASK SET status ' Authored' WHERE task_id = 2 を実行すると、トリガーが起動され、18+1 個のコミットされたタスクがステータスと共に表示されます。Authored
  • X+10ms の時点で、ユーザー B は UPDATE TASK1 SET status ' Authored' task_id = 4 を実行します。Authored
  • X+20ms の時点でユーザー A がコミット
  • X+30ms の時点でユーザー A がコミット
  • 最後に、 status を持つ 21 個のタスクがありますauthored。ただし、ジョブのステータスは変更されていません Authored(ただしAuthored、タスク数 = 20 の場合は変更する必要があります)。

このトラップを回避するには、トリガーSELECT null INTO dummy FROM JOBS WHERE job_id = :NEW.JOB_ID FOR UPDATE;の一部で使用して、after each rowアクセスをシリアル化するために JOBS テーブルの対応するレコードにロックを設定します (上記の例ではコメントされています)。
しかし、これが正しい解決策であるかどうかはまだわかりません。現時点では想像も予測もできないシナリオでデッドロックが発生する可能性があります。

于 2015-05-25T23:55:11.840 に答える
0

要するに、Oracleでは、トリガーでは、select fromトリガーが構築されているテーブルを作成できません。そうしないと、テーブルの変更エラーが発生する可能性があります。

いくつかのオプションがあります:

1)トリガーはまったくありません - 私の謙虚な意見では、これが最善です。私はこのようにします(しかし、おそらくトピックはより広く、すべてを知っているわけではありません)次のようなトリガーの必要性を置き換えるビューを作成します。

create or replace view v_jobs_done as 
  select * from jobs where not exists 
    select 1 from tasks 
      where TASKS.JOB_ID = jobs.JOB_ID
        and TASKS.TASK_STATUS not in ('Authored','Completed')

2)値を増やす代わりに値を減らします。したがって、jobs.tasks_count がゼロに達すると、すべてが完了したことがわかります。この場合、他のトリガーを構築/再構築する必要があります。

3)あなたに近い提案 -最新の 複合トリガーを使用できます- ここではパフォーマンスに疑問がありますが、機能します:

create or replace trigger Job_Authored
for update of task_status on tasks compound trigger

  type t_ids   is table of tasks.job_id%type;
  type t_cnts  is table of number;
  type t_job_counts is table of number index by varchar2(10);
  v_ids        t_ids;
  v_cnts       t_cnts;
  v_job_counts t_job_counts;

before statement is
  begin
    select job_id, count(1) 
      bulk collect into v_ids, v_cnts
      from tasks where tasks.task_status in ('Authored','Completed')
      group by job_id;

    for i in 1..v_ids.count() loop
      v_job_counts(v_ids(i)) := v_cnts(i);
    end loop;
  end before statement;

after each row is
  begin
    if :new.task_status = 'Authored' then 
      update jobs set job_status='Authored'
        where job_id = :new.job_id
          and task_count = v_job_counts(:new.job_id);
    end if;
  end after each row;
end Job_Authored;
于 2015-05-25T23:21:34.630 に答える