3

私は、Oracle の一連のデータベース テーブルに取り組んでおり、わずかにポリモーフィックなデータで参照整合性を強制する方法を見つけようとしています。

具体的には、さまざまなテーブルがたくさんあります。たとえば、リンゴ、バナナ、オレンジ、みかん、ブドウ、その他 100 種類の果物があるとします。今、果物を含む手順の実行を説明する表を作成しようとしています。そこで、「Apple ID 100 を食べる」という行を挿入し、次に「バナナの皮をむく ID 250」という行を挿入し、次に「タンジェリン ID 500 を冷蔵する」という行を挿入します。

これまでは、次の 2 つの方法でこれを行ってきました。

1 - 考えられる果物の種類ごとに列を含めます。チェック制約を使用して、1 つの列を除くすべての列が NULL であることを確認してください。外部キーを使用して、フルーツへの参照整合性を確保します。したがって、私の架空の例では、列ACTION, APPLEID, BANANAID, ORANGEID, TANGERINEID、およびを持つテーブルがありますGRAPEID。最初のアクションには、 row があり'Eat', 100, NULL, NULL, NULL, NULL, NULLます。2 番目のアクションについては、'Peel', NULL, 250, NULL, NULL, NULL. などなど

このアプローチは、オラクルの RI の利点をすべて自動的に取得するには優れていますが、100 種類の果物に拡張することはできません。列が多すぎて実用的ではなくなります。どの種類の果物を扱っているかを把握するだけでも、難しい問題になります。

2 - 果物の名前を含む列と、果物 ID を含む列を含めます。これも機能しますが、Oracleにデータの有効性を何らかの方法で強制させる方法(AFAIK)はありません。したがって、列はACTION, FRUITTYPE、および になりFRUITIDます。行データは'Eat', 'Apple', 100、その後'Peel', 'Banana', 250などになります。しかし、誰かが Apple ID 100 を削除したり、'Eat', 'Apple', 90000000その ID を持つ Apple を持っていなくても、ステップを挿入したりすることを妨げるものは何もありません。

個々の果物の種類ごとに個別の列を維持することを避けながら、外部キーのほとんどの利点を維持する方法はありますか? (または、技術的には、何らかの巧妙なトリックで複雑さを隠すことができれば、100 列を使用することを確信できます。日常の使用で正気に見える必要があります。)

明確化:私たちの実際のロジックでは、「果物」は、共通性がほとんどないまったく異なるテーブルです。顧客、従業員、会議、部屋、建物、資産タグなどを考えてみてください。ステップのリストは自由形式であり、ユーザーがこれらのいずれかに対するアクションを指定できるようにする必要があります。これらの無関係なものをそれぞれ含むテーブルが 1 つあれば、問題はありませんが、非常に奇妙な設計になってしまいます。

4

1 に答える 1

6

TASKS テーブルで FRUIT_TYPE を特定する必要がある理由がよくわかりません。一見すると、これは貧弱な (非正規化された) データ モデルです。

私の経験では、この種のデータをモデル化する最良の方法は、一般的なもの (例では FRUIT) のスーパータイプと詳細 (APPLE、GRAPE、BANANA) のサブタイプを使用することです。これにより、各インスタンスの特定の属性を記録しながら、共通の属性を 1 か所に保存できます。

スーパータイプのテーブルは次のとおりです。

create table fruits
    (fruit_id number not null
         , fruit_type varchar2(10) not null
         , constraint fruit_pk primary key (fruit_id)
         , constraint fruit_uk unique (fruit_id, fruit_type)
         , constraint fruit_ck check (fruit_type in ('GRAPE', 'APPLE', 'BANANA'))
    )
/

FRUITS には、主キーと複合一意キーがあります。複合キーは面倒なので、外部キー制約で使用する主キーが必要です。そうでない場合を除いて、これらのサブタイプ テーブルの状況です。サブタイプの FRUIT_TYPE の値を制約することで、GRAPES テーブル内のレコードがタイプ「GRAPE」などの FRUITS レコードにマップされることを保証できるため、ここでは一意のキーを参照として使用します。

create table grapes
    (fruit_id number not null
         , fruit_type varchar2(10) not null  default 'GRAPE'
         , seedless_yn  not null char(1) default 'Y'
         , colour varchar2(5) not null
         , constraint grape_pk primary key (fruit_id)
         , constraint grape_ck check (fruit_type = 'GRAPE')
         , constraint grape_fruit_fk foreign key (fruit_id, fruit_type)
                references fruit  (fruit_id, fruit_type)
         , constraint grape_flg_ck check (seedless_yn in ('Y', 'N'))
    )
/

create table apples
    (fruit_id number not null
         , fruit_type varchar2(10) not null
         , apple_type  varchar2(10) not null default 'APPLE'
         , constraint apple_pk primary key (fruit_id)
         , constraint apple_ck check (fruit_type = 'APPLE')
         , constraint apple_fruit_fk foreign key (fruit_id, fruit_type)
                references fruit  (fruit_id, fruit_type)
         , constraint apple_type_ck check (apple_type in ('EATING', 'COOKING', 'CIDER'))
    )
/

create table bananas
    (fruit_id number not null
         , fruit_type varchar2(10) not null default 'BANANA'
         , constraint banana_pk primary key (fruit_id)
         , constraint banana_ck check (fruit_type = 'BANANA')
         , constraint banana_fruit_fk foreign key (fruit_id, fruit_type)
                references fruit  (fruit_id, fruit_type)
    )
/

11g では、FRUIT_TYPE をサブタイプの仮想列にして、チェック制約をなくすことができます。

そこで、タスクの種類 (「皮をむく」、「冷蔵する」、「食べる」など) のテーブルが必要です。

create table task_types
    (task_code varchar2(4) not null
     , task_descr varchar2(40) not null
     , constraint task_type_pk primary key (task_code)
    )
/

実際の TASKS テーブルは、FRUITS と TASK_TYPES の単純な共通部分です。

create table tasks
    (task_code varchar2(4) not null
     , fruit_id number not null
     , constraint task_pk primary key (task_code, fruit_id)
     , constraint task_task_fk ask foreign key (task_code)
            references task_types (task_code)
     , constraint task_fruit_fk foreign key (fruit_id)
            references fruit (fruit_id)
/

これでニーズが満たされない場合は、質問を編集して詳細情報を含めてください。


「…果物ごとに異なるタスクが必要な場合は…」

はい、それがOPの投稿されたデザインの根底にある動機であるかどうか疑問に思いました. しかし、通常、ワークフローはそれよりもはるかに困難です。すべての果物に適用されるタスクもあれば、束になっている果物にのみ適用されるタスクもあれば、バナナにのみ関連するタスクもあります。


「私たちの実際のロジックでは、「成果」は、共通性がほとんどないまったく異なるテーブルです。顧客、従業員、会議、部屋、建物、資産タグなどを考えてください。ステップのリストは自由形式であると想定されており、ユーザーがこれらのいずれかに対するアクションを指定します。」

したがって、既存のテーブルがたくさんあります。これらのテーブルのレコードをタスクにフリーホイール スタイルで割り当てることができるようにする必要がありますが、タスクを所有する特定のレコードの識別を保証できるようにする必要があります。

タスク内のアクターの ID を保持するための一般的なテーブルが必要だと思いますが、何らかの方法でそれを他のテーブルにリンクする必要があります。これが私がそれにアプローチする方法です:

既存のテーブルの例:

create table customers
    (cust_id number not null
         , cname varchar2(100) not null
         , constraint cust_pk primary key (fruit_id)
    )
/

create table employees
    (emp_no number not null
         , ename varchar2(30) not null
         , constraint emp_pk primary key (fruit_id)
    )
/

アクターを保持する汎用テーブル:

create table actors
    (actor_id number not null
         , constraint actor_pk primary key (actor_id)
    )
/

ここで、既存のテーブルを新しいテーブルに関連付ける交差テーブルが必要です。

create table cust_actors
    (cust_id number not null
         , actor_id number not null
         , constraint cust_actor_pk primary key (cust_id, actor_id)
         , constraint cust_actor_cust_fk foreign key (cust_id)
                references customers (cust_id)
         , constraint cust_actor_actor_fk foreign key (actor_id)
                references actors (actor_id)
    )
/

create table emp_actors
    (emp_no number not null
         , actor_id number not null
         , constraint emp_actor_pk primary key (emp_no, actor_id)
         , constraint emp_actor_emp_fk foreign key (emp_no)
                references eployees (emp_no)
         , constraint cust_actor_actor_fk foreign key (actor_id)
                references actors (actor_id)
    )
/

以前に行ったことを考えると、TASKS テーブルはかなり驚くべきものではありません。

create table tasks
    (task_code varchar2(4) not null
     , actor_id number not null
     , constraint task_pk primary key (task_code, actor_id)
     , constraint task_task_fk ask foreign key (task_code)
            references task_types (task_code)
     , constraint task_actor_fk foreign key (actor_id)
            references actors (actor_id)
/

これらの交差テーブルはすべてオーバーヘッドが大きいように見えることに同意しますが、外部キー制約を強制する他の方法はありません。追加の問題は、CUSTOMERS でレコードを作成するたびに ACTORS および CUSTOMER_ACTORS レコードを作成することです。削除についても同様です。唯一の良いニュースは、必要なすべてのコードを生成できることです。

このソリューションは、100 個のオプションの外部キーを持つテーブルよりも優れていますか? そうではないかもしれませんが、それは好みの問題です。しかし、外部キーがまったくないよりはましです。データベースの実践に普遍的な真実があるとすれば、それは次のとおりです。リレーショナル整合性を強制するためにアプリケーションコードに依存しているデータベースは、間違った親を参照している、または親をまったく参照していない子でいっぱいのデータベースです。

于 2012-01-11T13:04:39.583 に答える