讓我們來看看它一分鐘這樣。你已經知道一些這個。 (PostgreSQL語法; dbms對於規範化並不重要,僅用於實現。)
create table geo (
zip_code char(5) not null,
-- CHECK constraints on lat and long omitted.
latitude float not null,
longitude float not null,
primary key (zip_code),
unique (latitude, longitude)
);
create table users (
user_id integer not null,
username varchar(10) not null,
zip_code char(5) not null,
primary key (user_id),
foreign key (zip_code) references geo (zip_code)
on update cascade on delete restrict
);
很明顯,這兩個表都在5NF。
您可以創建地理表中的ID號,你可以與該ID號取代users.zip_code。但用代理id號代替真實數據與標準化沒有任何關係,也不會改變這些表的標準格式。
用id號代替真實數據確實改變性能;每次您需要用戶的郵政編碼時,您都需要進行連接才能獲得。這不是一個完全可預見的變化;實際性能隨着dbms,服務器,鍵的寬度等而變化。你不應該有麻煩測試你自己的表。您可能會發現,多達數百萬行,自然鍵比代理ID號執行得更好。 (這是我在這裏測試我們生產數據庫的設計時發現的。)
現在讓我們稍微改變一下結構。
create table geo (
zip_code char(5) not null,
-- CHECK constraints on lat and long omitted.
latitude float not null,
longitude float not null,
primary key (zip_code),
unique (latitude, longitude),
-- Allows all three columns to be the target for a foreign key.
unique (zip_code, latitude, longitude)
);
create table users (
user_id integer not null,
username varchar(10) not null,
zip_code char(5) not null,
latitude float not null,
longitude float not null,
primary key (user_id),
-- This FK has to reference all three columns. If split, it's possible
-- to reference the latitude and longitude for the wrong zip code.
foreign key (zip_code, latitude, longitude)
references geo (zip_code, latitude, longitude)
on update cascade on delete restrict
);
雖然這種變化確實引入傳遞性依賴,在USER_ID - > ZIP_CODE和ZIP_CODE - >緯度等,它不會引起任何插入,更新或刪除異常。這是因爲傳遞依賴關係中涉及的所有值都由對5NF表的單個外鍵引用覆蓋。
表格geo仍然在5NF;用戶現在在2NF。我們在這裏獲得或失去了什麼?
- 我們丟失磁盤空間來存儲更廣泛的外鍵數據和 索引。
- 達到一定數量的行(可能數百萬),我們在 SELECT查詢中獲得更快的性能。 (我沒有測試你的模式,因爲我沒有時間,但我使用自然鍵測量了20到30倍的速度增加。您的 差異可能不會顯着)
- 我們在INSERT語句和大多數UPDATE 語句上獲得較慢的性能。 (較慢並不意味着慢。5毫秒比3毫秒, 但慢5毫秒不一定慢。大多數我自己的插入和更新的毫秒級運行 。)
所以構建一個測試模式,用幾百萬行填充它,並測量性能。測試與
- 上ZIP_CODE外鍵的性能,並且加入以獲得緯度和 經度,然後
- 調整和測試與{ZIP_CODE,緯度,經度}外鍵,然後
- 使用代理ID號和連接進行重組和測試,以獲得zip_code,緯度和經度。
並在此處發佈結果。我很想看到他們。
基準是一個很好的主意。 –