4

次の要件があります。

表1:複合主キーversion, id

------------------------------------
version   id     col1  coll2  active
------------------------------------
 1        123    'A'   'B'     'N'
 2        123    'C'   'D'     'Y'
 1        124    'E'   'F'     'Y'

table1これで、特定のの挿入と更新についてid、次の属性(トリガーによって派生)を使用して新しい行を作成する必要があります。

  1. Versionid与えられたとのために1ずつインクリメントする必要があります
  2. 最新の行がアクティブになるはずです(active列はに設定されていますY

例えば

INSERT INTO table1(id, col1, col2) VALUES (123, 'X', 'Y');

------------------------------------
version   id     col1  coll2  active
------------------------------------
 1        123    'A'   'B'     'N'
 2        123    'C'   'D'     'N'
 3        123    'X'   'Y'     'Y' 
 1        124    'E'   'F'     'Y'

3行目が作成されます

UPDATE table1 SET col1 = 'F' WHERE id = 124;

------------------------------------
version   id     col1  coll2  active
------------------------------------
 1        123    'A'   'B'     'N'
 2        123    'C'   'D'     'N'
 3        123    'X'   'Y'     'Y' 
 1        124    'E'   'F'     'N'
 2        124    'F'   'F'     'Y'

最後の行が作成されます

DELETE FROM dbo.table1 WHERE id = 124;

------------------------------------
version   id     col1  coll2  active
------------------------------------
 1        123    'A'   'B'     'N'
 2        123    'C'   'D'     'N'
 3        123    'X'   'Y'     'Y' 
 1        124    'E'   'F'     'N'
 2        124    'F'   'F'     'N'

ID124のすべての行が非アクティブになります。

これはモデリングの問題のようですが、TABLE1とサポートトリガーを使用してこの機能を提供することが義務付けられています。

必要に応じてテーブルの問題を変更してからselect max(version) 同じテーブルに挿入することはできません。回避策を提案していただけますか?

4

2 に答える 2

5

私はデータベースの設計が好きではありません、それはひどいです、しかしあなたはあなたがそれに固執していると言います。INSTEAD OFトリガーでビューを使用するのはどうですか?

1)テーブルの名前をTABLE1_BASEなどに変更します

2)ビューTABLE1をSELECT *FROMTABLE1_BASEとして作成します

3)次のようなINSTEADOFトリガーを追加します。

create trigger table1_io
instead of insert or update or delete on table1
for each row
begin
  if inserting or updating then 
      update table1_base
      set active = 'N'
      where id = :new.id
      and active = 'y';

      insert into table1_base...;
  elsif deleting then
      update table1_base
      set active = 'N'
      where id = :old.id
      and active = 'y';
  end if;
end;

(そんな感じ)

于 2012-08-29T12:38:08.927 に答える
4

あなたが言うように、これはモデリングの問題であり、テーブルの変更エラーは通常、これを達成するためのより良い方法があることを示しています。

これを回避する方法はいくつかありますが、いずれもベストプラクティスではなく、追加された複雑さをビジネスロジック/テーブルの重要性と比較検討する必要があります。これがキーテーブルである場合、単純な挿入である必要があるものに複雑なロジックを使用したいですか?

方法1 更新をパッケージプロシージャのテーブルに配置します。このプロシージャを呼び出すトリガーからジョブを実行します。これは、元の挿入とフォローアップの2つのトランザクションがあることを意味します。

方法2(1と同様) 必要な変更をカプセル化する高度なキューとオブジェクトタイプを作成します。挿入中にキューに追加します。オブジェクトをデキューし、必要に応じて変更を適用します。あなたの場合:すぐに。方法1と2には、フォローアップのクリーンアップのために最初のトランザクションが停止されないという利点があります。

方法3(機能しますが、複雑さのためにベストプラクティスではありません) 2つのトリガーが必要です。各行のテーブルへの挿入後とテーブルへの挿入後です。

どちらのトリガーも、ロジックをカプセル化したパッケージを呼び出します。これは次のようになります。

   CREATE OR REPLACE PACKAGE BODY XYZ AS
   TYPE t_template_rec
   IS
      RECORD (case_id     NUMBER (10), objective_id NUMBER (10));

   TYPE t_template_table IS TABLE OF t_template_rec;

   g_change_table t_template_table
         := t_template_table () ;

   TYPE t_deliv_rec IS RECORD (case_id NUMBER (10), stage_id NUMBER (10));

   TYPE t_deliv_table IS TABLE OF t_deliv_rec;

   g_deliv_table                         t_deliv_table := t_deliv_table ();

テーブルレベルのトリガーは、主キーとデータをこのプロシージャに渡します

   PROCEDURE CHECK_FOR_TEMPLATES (case_id_in     IN NUMBER,
                                  objective_in   IN NUMBER)
   IS

   BEGIN

      g_change_table.EXTEND;
      g_change_table (g_change_table.LAST).case_id := case_id_in;
      g_change_table (g_change_table.LAST).objective_id := objective_in;
   END CHECK_FOR_TEMPLATES;

行レベルのトリガーは、同じパッケージ内の別のプロシージャを呼び出します

   PROCEDURE ADD_TEMPLATES_ON_STATEMENT
   IS

      v_count          NUMBER (10);
      v_case_id        NUMBER (10);
      v_objective_id   NUMBER (10);
   BEGIN
      FOR i IN g_change_table.FIRST .. g_change_table.LAST
      LOOP
         SELECT   COUNT ( * )
           INTO   v_count
           FROM   XYZ.DELIVERABLE_TEMPLATE dt
          WHERE   DT.INITIATIVE_OBJECTIVE_ID =
                     g_change_table (i).objective_id;
         --check if there is any work to be done
         IF v_count > 0
         THEN
            v_case_id := g_change_table (i).case_id;
            v_objective_id := g_change_table (i).objective_id;
            --do some work here
         END IF;
      END LOOP;

      g_change_table.delete;
  END ADD_TEMPLATES_ON_STATEMENT;

わかりやすくするために、コメント/デバッグ/アサートを削除しました。私は方法#3を使用しており、それが機能するようになると正常に機能しましたが、戻ってそのテーブルにさらにビジネスロジックを実装する必要がある場合は、注意してアプローチします。

于 2012-08-29T12:36:43.547 に答える