7

ばかげた例を見てみましょう: 私は多くの家畜を飼っています。それぞれに id としての名前とタイプ (猫または犬) があり、このように書きましょう (擬似コード):

TABLE ANIMALS (
  NAME char,
  ANIMAL_TYPE char {'DOG', 'CAT'}
  PRIMARY KEY(NAME)
)

(たとえば、私は Felix という名前の CAT と Pluto という名前の犬を飼っています)

別のテーブルに、それぞれの動物の好みの餌を保存したいと思います。

TABLE PREFERED_FOOD (
  ANIMAL_NAME char,
  PREF_FOOD char
  FOREIGN KEY (ANIMAL_NAME) REFERENCES ANIMALS(NAME)
)

(たとえば、フェリックスは牛乳が好きで、プルートは骨が好きです)

可能な好ましい食品のセットを定義したいので、動物の種類ごとに食品の種類を 3 番目のテーブルに保存します。

TABLE FOOD (
  ANIMAL_TYPE char {'DOG', 'CAT'},
  FOOD_TYPE char
)

(例えば、犬は骨と肉を食べ、猫は魚と牛乳を食べます)

ここに私の質問があります: PREFERED_FOOD に外部制約を追加したいので、PREF_FOOD は FOOD.ANIMAL_TYPE=ANIMALS.TYPE の FOOD からの FOOD_TYPE です。PREFERED_FOOD で ANIMAL_TYPE を複製せずに、この外部キーを定義するにはどうすればよいですか?

私は SQL の専門家ではないので、本当に簡単なことなら、私を愚か者と呼んでもかまいません ;-)

4

5 に答える 5

3

SQLではできません。SQL がアサーションをサポートしていれば、できると思います。(SQL-92 標準で定義されたアサーション。私の知る限り、まだ誰もサポートしていません。)

この問題を回避するには、重複する制約を使用します。

-- Nothing special here.
create table animal_types (
  animal_type varchar(15) primary key
);

create table animals (
  name varchar(15) primary key,
  animal_type varchar(15) not null references animal_types (animal_type),
  -- This constraint lets us work around SQL's lack of assertions in this case.
  unique (name, animal_type)
);

-- Nothing special here.
create table animal_food_types (
  animal_type varchar(15) not null references animal_types (animal_type),
  food_type varchar(15) not null,
  primary key (animal_type, food_type)
);

-- Overlapping foreign key constraints.
create table animals_preferred_food (
  animal_name varchar(15) not null,
  -- This column is necessary to implement your requirement. 
  animal_type varchar(15) not null,
  pref_food varchar(10) not null,
  primary key (animal_name, pref_food),
  -- This foreign key constraint requires a unique constraint on these
  -- two columns in "animals".
  foreign key (animal_name, animal_type) 
    references animals (animal_name, animal_type),
  -- Since the animal_type column is now in this table, this constraint
  -- is simple.
  foreign key (animal_type, pref_food) 
    references animal_food_types (animal_type, food_type)
);
于 2012-09-25T00:37:47.950 に答える
0

使用している DBMS によっては (これを含めるように質問を編集してください)、列ANIMAL_TYPEPREFERED_FOOD列に一意の制約を作成することをお勧めします。

このようなもの:

ALTER TABLE PREFERED_FOOD
ADD CONSTRAINT uc_FoodAnimal UNIQUE (ANIMAL_TYPE,PREFERED_FOOD)
于 2012-09-24T21:27:25.467 に答える
0

率直に言って、私はあなたの要求に従うのに苦労しましたが、動物とその食べ物を表す簡単なモデルはおそらく次のようになります:

ここに画像の説明を入力

SPECIES_FOOD は特定の種が食べることができるすべての食物をリストし、INDIVIDUAL はその中の 1 つを Preferred_FOOD_NAME フィールドから選択します。

INDIVIDUAL.SPECIES_NAME は SPECIES と SPECIES_FOOD の両方に対する FKであるため、個体はその種が食べられない食品を好むことは決してありません。

もちろんこれは、個々の動物が複数の好みの食物を持つことができないことを前提としています。1また、何も指定できないことも想定しています。そうでない場合は、INDIVIDUAL.PREFERRED_FOOD_NAME を NOT NULL にします。

INDIVIDUAL_NAME は意図的にキーにしなかったため、たとえば「Felix」という名前の猫を 2 匹飼うことができます。それが望ましくない場合は、適切なキーを簡単に追加できます。

食品について知る必要があるのはその名前だけで、どの種からも独立して食品を表す必要がない場合は、FOOD テーブルを完全に省略できます。


1個々の動物ごとに複数の好みの食物が存在する可能性がある場合、INDIVIDUAL と SPECIES_FOOD の間にもう 1 つのテーブルが必要であり、識別関係を使用し続けるように注意する必要があります。種によって食べられない)。

于 2012-09-25T01:20:03.680 に答える
0

ANIMALS と Preferred_FOOD の (自然な) JOIN を取ると、各動物の種類と好みの食べ物がリストされたテーブルが得られます。

その組み合わせを個々の動物ごとに「有効」にする必要があります。「有効」とは、「FOOD にリストされている有効な動物の種類/食品の種類の組み合わせの列挙に表示されること」を意味します。

したがって、FK に多少似た制約がありますが、今回は "外部キー" がベース テーブルではなく、2 つのテーブルの結合に表示されます。このタイプの制約の場合、SQL 言語には CHECK 制約と ASSERTIONS があります。

ASSERTION バージョンは最も単純です。これは次のような制約です (要点をわかりにくくする単なる属性の名前変更を避けるために、私は属性名を多少自由にしています)

CREATE ASSERTION <name for your constraint here>
 CHECK NOT EXISTS (SELECT ANIMAL_TYPE, FOOD_TYPE
                     FROM ANIMALS NATURAL JOIN PREF_FOOD
                    WHERE (ANIMAL_TYPE, FOOD_TYPE) NOT IN
                          SELECT ANIMAL_TYPE, FOOD_TYPE FROM FOOD_TYPE);

しかし、平均的な SQL エンジンは ASSERTION をサポートしていません。したがって、CHECK 制約を使用する必要があります。たとえば、PREF_FOOD テーブルの場合、必要な CHECK 制約は次のようになります。

CHECK EXISTS (SELECT 1
                FROM FOOD NATURAL JOIN ANIMAL
               WHERE ANIMAL_TYPE = <animal type of inserted row> AND
                     FOOD_TYPE = <food type of inserted row>);

理論的には、これで制約を適用するのに十分なはずですが、制約が定義されているテーブル以外のテーブルへの参照があるため、平均的な SQL エンジンはこの種の CHECK 制約をサポートしません。

したがって、あなたが持っているオプションは、キャットコールのようなかなり複雑な(*)セットアップに頼るか、トリガーを使用して制約を適用することです(そして、かなり多くのトリガーを書く必要があります(少なくとも3つまたは6つ、これを考えていません)次に最適なオプションは、アプリケーション コードでこれを強制することです。この場合も、同じ数の個別のチェックを実装する必要がある場所が 3 つまたは 6 つ (多かれ少なかれ) あります。

これら 3 つのシナリオのすべてにおいて、制約の存在とその内容を別の場所に文書化することをお勧めします。3 つのどれも、この設計を読んでいる第三者に、これが一体何なのかを明らかにするものはありません。

(*) 「複雑」という言葉は正確には正しくないかもしれませんが、そのようなソリューションは意図的な冗長性に依存しているため、意図的に設計で 3NF を下回ることに注意してください。つまり、ユーザーがデータベースを更新して一貫性を保つことが難しくなります (意図的な冗長性のため)。

于 2012-09-26T13:19:49.200 に答える
0
FOREIGN KEY (PREF_FOOD) REFERENCES FOOD (FOOD_TYPE)

これにより、Preferred_FOOD テーブル内のすべての PREFFOOD が FOOD テーブルの FOOD_TYPE に既に存在することが確認されます。

そしてFOODテーブルの使用では、今では一目瞭然です。

FOREIGN KEY (ANIMAL_TYPE) REFERENCES ANIMALS (ANIMAL_TYPE)
于 2012-09-24T21:25:17.863 に答える