1

私は4つのテーブルを持っています。

PERSON     DELIVERY_MAPPING       GENERATION_SYSTEM       DELIVERY_METHOD
------     ----------------       -----------------       ---------------
ID         PERSON_ID              ID                      ID
NAME       GENERATION_SYSTEM_ID   NAME                    NAME
                                  DELIVERY_METHOD_ID      IS_SPECIAL

サンプルデータ:

PERSON     DELIVERY_MAPPING       GENERATION_SYSTEM       DELIVERY_METHOD
------     ----------------       -----------------       ---------------
1. TOM       1    1               1. COLOR PRINTER 1      1. EMAIL    N
2. DICK      1    2               2. BW PRINTER    1      2. POST     N
3. HARRY     2    3               3. HANDWRITTEN   3      3. PIGEONS  Y

ADELIVERY_METHODには、新しい文字を配信する方法が含まれています — EMAILPOSTPIGEON. 列はIS_SPECIAL速達の手段として記録をマークします。Yまたはの単純な値で示されますN。のみPIGEON、特殊な発送方法ですY、他はありませんN

GENERATION_SYSTEMは、最終的にレターを印刷するための情報があります。値の例はCOLOR PRINTERDOT MATRIX PRINTERです。それぞれGENERATION_SYSTEMは常にDELIVERY_METHODのいずれかを使用して配信されます。との間に外部キーがGENERATION_SYSTEMありDELIVERY_METHODます。

現在、それぞれPERSONが異なる によって生成された文字を持つことができます。GENERATION_SYSTEMこれは多対多の関係であるため、DELIVERY_MAPPINGテーブルがあり、そのために両端に外部キーがあります。

ここまでは順調ですね。

ある人が特別な配信方法を使用するシステムによって生成された手紙を持っている場合、マッピングリストに複数の生成システムを持つことは許可されないことを確認する必要があります. たとえば、ディックはすでにすべての手書きの手紙を鳩によって配達されているため (これは特別な配達方法としてマークされています)、カラー プリンターを使用して手紙を作成することはできません。

どうすればそのような制約を達成できますか? テーブルで before-insert-or-update トリガーを使用して実行しようとしましたDELIVERY_MAPPINGが、更新時に変更トリガーの問題が発生します。

このシナリオをさらに正常化できますか? テーブルを適切に正規化していないだけかもしれません。

いずれにせよ、この問題に対するあなたの見解を聞きたいです。私は十分に冗長であることを願っています (...そして、この投稿のより良いタイトルを提案していただければ、それは素晴らしいことです)

4

3 に答える 3

1

このような複雑な制約には、トリガーを使用する必要があると思います。変更テーブルの問題は問題ではないと思います。更新を行うか、何もしないかのどちらかだからです。

心配する必要がある唯一のテーブルは ですDelivery_Mapping。このテーブルへの変更を許可する前に、既存のテーブルでクエリを実行して、スペシャルと gs の数を取得する必要があります。

select SUM(case when dme.is_special = 'Y' then 1 else 0 end) as NumSpecial,
       count(distinct gs.id) as NumGS,
       MIN(gs.id) as GSID
from delivery_mapping dm join
     generation_system gs
     on dm.generation_system_id = gs.id join
     delivery_method dme
     on gs.delivery_method_id = dme.id
where dm.person_id = PERSONID

この情報を使用して、挿入/更新を続行できるかどうかを確認できます。条件を確認する必要があると思います:

  • NumSpecial = 0 で、新しい配送方法が特別でない場合は、次に進みます。
  • NumSpecial = 0 かつ NumGS = 0 の場合は、次に進みます。
  • それ以外の場合は失敗します。

更新のロジックはもう少し複雑です。

ちなみに、私は更新/挿入/削除をストアド プロシージャにラップすることを好みます。そのため、このようなロジックはトリガーに隠されません。プロシージャのデバッグと保守は、カスケードしている可能性のあるトリガーを処理するよりもはるかに簡単であることがわかりました。

于 2013-01-24T14:15:20.130 に答える
1

シリアル化を保証できない限り、このためのベース テーブルでのトリガーは避けます。

Gordon が言うように API (最善の方法) を使用できます (再度、必ずシリアル化してください)。それが適切でない場合は、マテリアライズド ビューを使用します (コミット時にチェックが行われるため、ここでシリアル化する必要はありません)。 :

SQL> create materialized view log on person with rowid, primary key including new values;

Materialized view log created.

SQL> create materialized view log on delivery_mapping with rowid, primary key including new values;

Materialized view log created.

SQL> create materialized view log on generation_system with rowid, primary key (delivery_method_id) including new values;

Materialized view log created.

SQL> create materialized view log on delivery_method with rowid, primary key (is_special) including new values;

Materialized view log created.

各ユーザーの特別なリンクと特別でないリンクの数を表示する具体化されたビューを作成します。

SQL> create materialized view check_del_method
  2  refresh fast on commit
  3  with primary key
  4  as
  5  select pers.id, count(case del_meth.is_special when 'Y' then 1 end) special_count,
  6         count(case del_meth.is_special when 'N' then 1 end) non_special_count
  7    from person pers
  8         inner join delivery_mapping del_map
  9                 on pers.id = del_map.person_id
 10         inner join generation_system gen
 11                 on gen.id = del_map.generation_system_id
 12         inner join delivery_method del_meth
 13                 on del_meth.id = gen.delivery_method_id
 14   group by pers.id;

Materialized view created.

MView はコミット時の高速リフレッシュとして定義されているため、変更された行はコミット時に再構築されます。現在のルールは、特別なカウントと非特別なカウントがゼロ以外の場合、それはエラー状態であるということです。

SQL> create trigger check_del_method_aiu
  2  after insert or update on check_del_method
  3  for each row
  4  declare
  5  begin
  6    if (:new.special_count > 0 and :new.non_special_count > 0)
  7    then
  8      raise_application_error(-20000, 'Cannot have a mix of special and non special delivery methods for user ' || :new.id);
  9   end if;
 11  end;
 12  /

Trigger created.

SQL> set serverout on
SQL> insert into delivery_mapping values (1, 3);

1 row created.

SQL> commit;
commit
*
ERROR at line 1:
ORA-12008: error in materialized view refresh path
ORA-20000: Cannot have a mix of special and non special delivery methods for
user 1
ORA-06512: at "TEST.CHECK_DEL_METHOD_AIU", line 6
ORA-04088: error during execution of trigger 'TEST.CHECK_DEL_METHOD_AIU'
于 2013-01-24T17:25:00.560 に答える
0
 CREATE MATERIALIZED VIEW special_queues_mv
  NOLOGGING
  CACHE
  BUILD IMMEDIATE 
  REFRESH ON COMMIT 
  ENABLE QUERY REWRITE
     AS SELECT dmap.person_id
             , SUM(DECODE(dmet.is_special, 'Y', 1, 0)) AS special_queues
             , SUM(DECODE(dmet.is_special, 'N', 1, 0)) AS regular_queues
          FROM delivery_mapping dmap
             , generation_system gsys
             , delivery_method dmet
         WHERE dmap.generation_system_id = gsys.id
           AND gsys.delevery_method_id = dmet.id
         GROUP
            BY dmap.person_id
/

  ALTER MATERIALIZED VIEW special_queues_mv
    ADD ( CONSTRAINT special_queues_mv_chk1 CHECK ((special_queues = 1 AND regular_queues = 0) OR ( regular_queues > 0 AND special_queues = 0 ) ) ENABLE VALIDATE)
/

それが私がやった方法です。DazzaLさんの回答がヒントになりました。

于 2013-01-28T12:26:57.227 に答える