始終將單個整數列作為主鍵有什麼缺點?
在我正在處理的一個 Web 應用程序中,所有數據庫操作都使用一些在實體框架 ORM 上定義的通用儲存庫進行抽象。
但是,為了對通用儲存庫進行簡單設計,所有涉及的表都必須定義一個唯一的整數(
Int32
在 C# 中,int
在 SQL 中)。直到現在,這一直是桌遊的PK,也是IDENTITY
。外鍵被大量使用,它們引用這些整數列。它們對於一致性和 ORM 生成導航屬性都是必需的。
應用層通常執行以下操作:
- 從表中載入初始數據(*) -
SELECT * FROM table
- 更新-
UPDATE table SET Col1 = Val1 WHERE Id = IdVal
- 刪除-
DELETE FROM table WHERE Id = IdVal
- 插入-
INSERT INTO table (cols) VALUES (...)
不太頻繁的操作:
- 批量插入-
BULK INSERT ... into table
所有數據載入後跟 (*)(檢索生成的標識符)- 批量刪除- 這是一個正常的刪除操作,但從 ORM 的角度來看是“龐大的”:
DELETE FROM table where OtherThanIdCol = SomeValue
- 批量更新- 這是一個正常的更新操作,但從 ORM 的角度來看是“龐大的”:
UPDATE table SET SomeCol = SomeVal WHERE OtherThanIdCol = OtherValue
*所有小表都在應用程序級別記憶體,幾乎所有
SELECTs
都不會到達數據庫。一個典型的模式是初始載入和大量的INSERT
s、UPDATE
s 和DELETE
s。根據目前的應用程序使用情況,在任何表中達到 100M 記錄的可能性非常小。
問題: 從 DBA 的角度來看,有這個表設計限制我會遇到重大問題嗎?
$$ EDIT $$
在閱讀了答案(感謝您的回饋)和參考文章後,我覺得我必須添加更多細節:
目前應用程序細節- 我沒有提及目前的 Web 應用程序,因為我想了解該模型是否也可以用於其他應用程序。但是,我的特殊情況是從 DWH 中提取大量元數據的應用程序。源數據非常混亂(以一種奇怪的方式去規範化,有一些不一致,在許多情況下沒有自然標識符等),我的應用程序正在生成清晰的分離實體。此外,還會顯示許多生成的標識符 (
IDENTITY
),以便使用者可以將它們用作業務密鑰。除了大量的程式碼重構之外,這還排除了 GUID 的使用。“它們不應該是唯一標識一行的唯一方法”(Aaron Bertrand♦)——這是一個非常好的建議。我所有的表還定義了一個 UNIQUE CONSTRAINT 以確保不允許業務重複。
前端應用驅動設計與數據庫驅動設計- 設計選擇是由這些因素引起的
實體框架限制- 允許多列 PK,但它們的值不能更新
自定義限制- 具有單個整數鍵大大簡化了資料結構和非 SQL 程式碼。例如:所有值列表都有一個整數鍵和一個顯示值。更重要的是,它保證任何標記為記憶體的表都能夠放入
Unique int key -> value
映射中。複雜的選擇查詢——這幾乎不會發生,因為所有小(< 20-30K 記錄)表數據都在應用程序級別記憶體。這使得編寫應用程式碼時的生活有點困難(更難編寫 LINQ),但數據庫受到的影響要好得多:
列表視圖- 不會
SELECT
在載入時生成查詢(所有內容都被記憶體)或如下所示的查詢:SELECT allcolumns FROM BigTable WHERE filter1 IN (val1, val2) AND filter2 IN (val11, val12)
有其他必需的值都是通過記憶體查找 (O(1)) 獲取的,因此不會生成複雜的查詢。 **編輯視圖**- 將生成`SELECT`如下語句:
LECT allcolumns FROM BigTable WHERE PKId = value1
(所有過濾器和值都是`int`s)
除了額外的磁碟空間(以及記憶體使用和 I/O)之外,即使對不需要 IDENTITY 列的表(不需要 IDENTITY 列的表的範例)添加 IDENTITY 列也沒有任何害處是一個簡單的聯結表,例如將使用者映射到他/她的權限)。
我反對在 2010 年的部落格文章中盲目地將它們添加到每個表中:
但是代理鍵確實有有效的案例——請注意不要假設它們保證唯一性(這有時是它們被添加的原因——它們不應該是唯一標識行的***唯一方法)。***如果您需要使用 ORM 框架,並且您的 ORM 框架需要單列整數鍵,即使您的實際鍵不是整數,也不是單列,或者兩者都不是,請確保定義唯一約束/索引也可以用於您的真正鑰匙。