2012-05-04 40 views
24

在此係統中,我們存儲產品,產品圖像(產品可能有多個圖像)以及產品的默認圖像。數據庫:在SQL中,兩個表可以互相引用嗎?

CREATE TABLE `products` (
    `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `NAME` varchar(255) NOT NULL, 
    `DESCRIPTION` text NOT NULL, 
    `ENABLED` tinyint(1) NOT NULL DEFAULT '1', 
    `DATEADDED` datetime NOT NULL, 
    `DEFAULT_PICTURE_ID` int(10) unsigned DEFAULT NULL, 
    PRIMARY KEY (`ID`), 
    KEY `Index_2` (`DATEADDED`), 
    KEY `FK_products_1` (`DEFAULT_PICTURE_ID`), 
    CONSTRAINT `FK_products_1` FOREIGN KEY (`DEFAULT_PICTURE_ID`) REFERENCES `products_pictures` (`ID`) ON DELETE SET NULL ON UPDATE SET NULL 
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8; 


CREATE TABLE `products_pictures` (
    `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `IMG_PATH` varchar(255) NOT NULL, 
    `PRODUCT_ID` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`ID`), 
    KEY `FK_products_pictures_1` (`PRODUCT_ID`), 
    CONSTRAINT `FK_products_pictures_1` FOREIGN KEY (`PRODUCT_ID`) REFERENCES `products` (`ID`) ON DELETE CASCADE 
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; 

,你可以看到,products_pictures.PRODUCT_ID -> products.IDproducts.DEFAULT_PICTURE_ID -> products_pictures.ID,使循環引用。可以嗎?

+2

這應該但不能對它有信心。爲什麼不嘗試添加一些記錄,然後嘗試刪除它們。 – Deepesh

回答

34

不,它不好。表格之間的循環引用很混亂。看到這個(十年前)文章:SQL By Design: The Circular Reference

一些DBMS可以處理這些,並特別謹慎,但MySQL會有問題。


作爲您的設計,第一個選擇是使兩個FK中的一個可爲空。這可以讓你解決雞與雞的問題(我應該先插入哪個表?)。

儘管您的代碼存在問題。它將允許產品在該圖片引用其他產品的位置具有默認圖片!

要禁止這樣的錯誤,你的FK約束應該是:

CONSTRAINT FK_products_1 
    FOREIGN KEY (id, default_picture_id) 
    REFERENCES products_pictures (product_id, id) 
    ON DELETE RESTRICT       --- the SET NULL options would 
    ON UPDATE RESTRICT       --- lead to other issues 

這將需要在表products_pictures一個UNIQUE約束/指數(product_id, id)對於上述FK加以界定和正常工作。


另一種方法是,以除去Default_Picture_ID柱形成product表,並在picture表添加一個IsDefault BIT柱。這個解決方案的問題是如何讓每個產品只有一張圖片能夠讓所有其他圖片都可以使用。在SQL-服務器(我認爲在Postgres的)這可能與部分指標完成:

CREATE UNIQUE INDEX is_DefaultPicture 
    ON products_pictures (Product_ID) 
    WHERE IsDefault = 1 ; 

但MySQL有沒有這樣的功能。


第三種方法,它甚至允許你同時擁有FK列定義爲NOT NULL是使用延遲約束。這在PostgreSQL中有效,我認爲在Oracle中。通過@Erwin檢查此問題和答案:Complex foreign key constraint in SQLAlchemy所有關鍵列NOT NULL部分)。

MySQL中的約束不能推遲。


第四種方法(我發現最乾淨)是刪除Default_Picture_ID列並添加另一個表。在FK約束和所有FK列中沒有圓形路徑將是NOT NULL用該溶液:

product_default_picture 
---------------------- 
product_id   NOT NULL 
default_picture_id NOT NULL 
PRIMARY KEY (product_id) 
FOREIGN KEY (product_id, default_picture_id) 
    REFERENCES products_pictures (product_id, id) 

這還需要一個UNIQUE約束/表products_pictures(product_id, id)索引作爲溶液1


總之,使用MySQL,你有兩個選擇:

  • 選項1(可爲空的FK列)與上述修正實施完整性正確

  • 選項4(無可空FK列)

+1

@Killrawr:thnx –

+0

這是一個很棒的概述(+1),但我沒有看到有什麼優勢選項4已超過選項1.使用選項1,多功能應用程序代碼可以通過創建產品創建無效數據,並且永遠不會給它一個'default_picture_id'。使用選項4,多功能應用程序代碼可以通過創建產品類似地創建無效數據,並且從不爲其創建「product_default_picture」行。但是,選擇產品及其默認圖片現在需要更多的連接,並且模式的自我記錄較少。我們通過初始方法獲得了哪些好處以抵消額外的複雜性? –

+2

@MarkAmery再一次加入並不是一個問題,是嗎?當需要爲默認圖片存儲更多屬性時,會出現第四種解決方案的優點。這也可以在第一個解決方案中工作,並且可以使用空列,但是在這種情況下,MySQL無法強制使用CHECK約束(以確保所有這些額外的屬性都是NULL或NOT NULL)。 –

-1

不是循環裁判,那就是PK-FK

+0

我的意思是,「A」是指「B」,「B」是指「A」,是不是會傷害某些東西? –

+1

這裏沒有什麼突破,我相信你應該看看EF Codd的規則,如果可能的話,看看RDBMS – Satya

4

這只是建議,但如果有可能創造一個加入這個表之間的表可能是有益的跟蹤

product_productcat_join 
------------------------ 
ID(PK) 
ProductID(FK)- product table primary key 
PictureID(FK) - category table primary key 
2

你唯一的問題將要遇到的是當你插入。 你首先插入哪一個?

有了這個,你將不得不做這樣的事情:

  • 插入產品與空默認圖片
  • 插入圖片(S)與新創建的產品ID
  • 更新的產品設置默認圖片添加到您剛插入的圖片。

同樣,刪除不會很有趣。

0

約翰你做什麼心不是什麼壞事,但使用PK-FK實際上通過刪除冗餘重複數據歸數據幫助。已經從

  • 提高數據完整性由於重複的存儲位置的消除相同數據
  • 減少鎖定爭一些精彩的優點和改進的多用戶併發
  • 較小的文件
相關問題