このまましばらく様子を見てみましょう。あなたはすでにこれのいくつかを知っています. (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 であることは明らかです。
geo テーブルの ID 番号を作成し、users.zip_codeをその ID 番号に置き換えることができます。しかし、実際のデータをサロゲート 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 -> latitude などでは、挿入、更新、または削除の異常は発生しません。これは、推移的な依存関係に含まれるすべての値が、5NF テーブルへの単一の外部キー参照によってカバーされるためです。
テーブル geo はまだ 5NF です。ユーザーは現在 2NF です。私たちはここで何を得て、何を失ったのでしょうか。
- より広い外部キー データとインデックスを格納するためのディスク領域が失われます。
- ある程度の行数 (おそらく数百万行) までは、SELECT クエリのパフォーマンスが向上します。(時間がないので、スキーマをテストしませんでした。しかし、自然キーを使用して 20 倍から 30 倍の速度の向上を測定しました。違いはおそらくそれほど劇的ではありません。)
- INSERT ステートメントとほとんどの UPDATE ステートメントでパフォーマンスが低下します。(遅いからといって遅いという意味ではありません。5 ミリ秒は 3 ミリ秒よりも遅いですが、5 ミリ秒は必ずしも遅いとは限りません。私自身の挿入と更新のほとんどは、ミリ秒未満で実行されます。)
したがって、テスト スキーマを構築し、数百万行を入力して、パフォーマンスを測定します。でパフォーマンスをテストする
- zip_code の外部キー、および緯度と経度を取得するための結合、
- {zip_code, latitude, longitude} の外部キーで再構築してテストし、次に
- サロゲート ID 番号と結合を使用して再構築およびテストし、zip_code、緯度、および経度を取得します。
そして、ここに結果を投稿してください。ぜひ見てみたいです。