簡體   English   中英

具有來自不同表的多列的外鍵

[英]Foreign key with multiple columns from different tables

讓我們舉一個愚蠢的例子:我有很多家畜,每個都有一個NAME作為id和一個類型(CAT或DOG),讓我們這樣寫( 偽代碼 ):

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)
)

(例如,菲利克斯喜歡牛奶,冥王星喜歡骨頭)

由於我想定義一組可能的首選食物,我會在第三個表格中存儲每種動物的食物類型:

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

(例如,狗狗吃骨頭和肉,CAT吃魚和牛奶)

我的問題出現了:我想在PREFERED_FOOD中添加一個外部約束,因為PREF_FOOD是來自FOOD的FOOD_TYPE,其中FOOD.ANIMAL_TYPE = ANIMALS.TYPE。 如何在不復制PREFERED_FOOD上的ANIMAL_TYPE的情況下定義此外鍵?

我不是SQL的專家,所以如果真的很容易你可以叫我傻;-)

你不能在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)
);
FOREIGN KEY (PREF_FOOD) REFERENCES FOOD (FOOD_TYPE)

在PREFERRED_FOOD表中,這將確保PREFERRED_FOOD表中的每個PREFFOOD都已存在於FOOD表的FOOD_TYPE中。

而在FOOD表中使用,現在已經很明顯了。

FOREIGN KEY (ANIMAL_TYPE) REFERENCES ANIMALS (ANIMAL_TYPE)

根據您所使用的DBMS(請編輯您的問題,包括這一點),你可能會想創建的唯一約束ANIMAL_TYPEPREFERED_FOOD列。

像這樣的東西:

ALTER TABLE PREFERED_FOOD
ADD CONSTRAINT uc_FoodAnimal UNIQUE (ANIMAL_TYPE,PREFERED_FOOD)

坦率地說,我在遵循您的要求時遇到了一些麻煩,但是代表動物及其食物的簡單模型可能如下所示:

在此輸入圖像描述

SPECIES_FOOD列出了給定物種可以吃的所有食物,然后INDIVIDUAL只通過PREFERRED_FOOD_NAME字段選擇其中一種。

由於INDIVIDUAL.SPECIES_NAME對於SPECIES和SPECIES_FOOD 都是 FK, 因此個人永遠不會喜歡其物種不可食用的食物。

這當然假設個體動物不能有一種以上的優選食物。 1它還假設它沒有 - 如果不是這樣,只需使INDIVIDUAL.PREFERRED_FOOD_NAME NOT NULL即可。

INDIVIDUAL_NAME故意沒有成為鑰匙,所以你可以擁有兩只名為“Felix”的貓。 如果這不合適,您可以輕松添加相應的密鑰。

如果您需要了解食物的名稱,並且您不需要獨立於任何物種代表食物,則可以完全省略FOOD表。


1如果每個動物可以有多種首選食物,你需要在“個人”和“物種”之間再增加一個表,並且要小心繼續使用識別關系,這樣SPECIES_NAME就會一直向下遷移(以防止更喜歡食物)不適合該物種)。

如果您采用ANIMALS和PREFERRED_FOOD的(自然)JOIN,那么您將得到一張表格,其中列出了每只動物的類型和首選食物。

您希望該組合對每個動物都“有效”,其中“有效”表示“出現在FOOD中列出的有效動物類型/食物類型組合的列舉中”。

所以你有一個有點類似於FK的約束,但這次“外鍵”不出現在基表中,而是出現在兩個表的連接中。 對於此類約束,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約束,因為除了定義約束之外的表的引用。

所以你有的選擇是采用相當復雜的(*)設置,比如catcall,或者使用觸發器強制執行約束(你必須編寫相當多的(至少三個或六個,沒有想到這一點)詳細信息),您的下一個最佳選擇是在應用程序代碼中強制執行此操作,並且將再次存在三個或六個(更多或更少)不同的位置,其中需要實現相同數量的不同檢查。

在所有這三種情況中,您最好希望在其他地方記錄約束的存在,以及它究竟是什么。 對於閱讀此設計的第三方來說,三者中的任何一個都不會讓這一切變得非常明顯。

(*)“復雜”可能不是正確的單詞,但請注意,此類解決方案依賴於故意冗余 ,因此故意在設計下低於3NF 這意味着您的設計暴露於更新異常,這意味着用戶更難以更新數據庫並使其保持一致(正是因為故意的冗余)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM