邏輯數據庫設計 - 無視約束(談外鍵)

有一些開發人員不推薦使用完整性約束,你可能聽過以下這麼幾點不使用外鍵的原因。

  1、數據更新有可能和約束衝突。

  2、當前的數據庫設計如此靈活,以致於不支持引用完整性約束。

  3、數據庫爲外鍵建立的索引會影響性能。

  4、當前使用的數據庫不支持外鍵。

  5、定義外鍵的語法並不簡單,還需要查閱。

一、反模式:無視約束

  即使第一感覺告訴你,省略外鍵約束能使得數據庫設計更加簡單、靈活,或者執行更加高效,你還是不得不在其他方面付出相應的代價 -- 必須增加額外的代碼來手動維護引用完整性。

  1、完整性問題

  很多人對引用完整性的解決方案是通過編寫特定的程序代碼來確保數據間的關係的。每次插入新記錄時,需要確保外鍵列所引用的值在其對應的表中存在;每次刪除記錄時,需要確保所有相關的表都要同時合理地更新。

  爲了避免在沒有外鍵約束的情況下產生引用的不完整狀態,需要在任何改變生效前執行額外的select查詢,以此來確保這些改變不會導致引用錯誤。

  比如,在插入一條記錄前之前,需要檢查對應的被引用記錄是否存在:

select Account_Id from Account where Account_Id = 1

  然後才能執行Insert操作。

  要刪除一條記錄,需要先確認沒有別的記錄引用了該條記錄:

select bug_id from Bugs where reported_by = 1

  然後才能執行Delete操作。

  如果發生一個萬一情況,這個Account_Id恰巧在刪除操作的查詢和刪除語句的執行間隙插入了一條新的缺陷記錄,這該怎麼辦?

  2、級聯問題

  另外很多開發人員避免使用外鍵約束的理由,是因爲這些約束會使得更新多張表中相關聯的列變得比較麻煩。比如刪除一條被其他記錄依賴的數據時,不得不刪除所有的子記錄來避免違反外鍵約束:

  delete from Account where RoleId = 1

  delete from Role RoleId = 1

  另外還有update

  update Role set RoleId = 2
  update Account set Role = 2 where RoleId = 1

  說白了就是級聯更新與級聯刪除有麻煩,因此乾脆不用外鍵。

  如果有人說的話,聽起來像下面的意思,那麼他們可能使用了本文所描述的反模式。

  1、我要怎麼寫這個查詢語句來檢查一個值是否沒有同時在兩張表中存在。

  通常這樣的需要是爲了查找那些孤立的行,那些被遺忘的數據記錄行。

  2、有沒有一種簡單的方法來判斷在一張表中存在的數據是否也在第二張表中存在?

  這麼做是用來確認父記錄確實存在,外鍵會自動完成這些,並且外鍵會使用附表的索引儘可能搞笑地完成。

  3、外鍵?有人告訴我別用它,因爲那會影響數據庫的效率。

  性能總是用來裁剪設計的一個很好的理由,但總是會引入更多的問題,甚至包括性能問題本身。我之前就看到過,說增加外鍵 增刪改的速度會變慢的說法。但請記住,這種說法只適用於這張表是用於大規模增刪改的情況。對於一張主要就是用來大範圍增刪改的表,不用外鍵似乎無可厚非。但是如果一張表,增刪改並不多,這種說法就不成立了。相反,你避免了外鍵,卻不得不使用更多的手段去維護數據完整性。

  另外,大多數數據庫都提供了級聯更新與級聯刪除的功能。這個功能相信初學者都有所耳聞了。實際上,之前本文寫代碼的時,即使使用了級聯更新與刪除,但都是不放心,仍然在程序中,按照沒有級聯的情況,先執行一遍。對於此,我只想對自己說,既然使用了級聯功能,就相信它。如果你不相信它,還要自己寫代碼級聯。那就別用它。而事實上,我發現數據庫的級聯功能,比自己寫代碼維護要可靠得多。

  對於開銷方面,的確對增刪改需要那麼一點額外的系統開銷,但是同時另一方面賺會來的有:

  1、不需要在更新或刪除記錄前執行select進行檢查。

  2、在同步修改時不需要再鎖住整張表。

  3、不再需要執行定期的監控腳本來修正不可避免的垃圾數據,孤立數據。

  外鍵是一個使用很方便的東西,提高性能,還能幫助你在任何簡單或複雜的形式的數據變更下始終維持引用完整性。

  總結:客觀的分析,本人還是建議使用外鍵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章