Kodokon kodokon.com

データモデリング:正規化と非正規化

異常を取り除くためにスキーマを1NFから3NFへと構造化し、いつ意識的に非正規化すべきかを知りましょう。

10 分 · 3 問

このレッスンを Kodokon で開く

悪いスキーマは何年もあなたにツケを払わせます。正規化が目指すのはただ一つ、各事実が一度だけ保存されることです。それによって三つの異常を取り除きます。更新(一つのメールアドレスを十か所で変更する)、挿入(注文なしには商品を追加できない)、削除(最後の注文を消すと顧客も消える)です。わざと出来を悪くしたこのスキーマを見てください。

SQL
CREATE TABLE bad_orders (
  id INTEGER PRIMARY KEY,
  customer_name TEXT,
  customer_email TEXT,
  product_names TEXT,
  total REAL
);

INSERT INTO bad_orders
  (customer_name, customer_email,
   product_names, total)
VALUES
  ('Alice', 'alice@mail.com',
   'keyboard,mouse', 74.0),
  ('Alice', 'alice@mail.co',
   'monitor', 199.0);
一つの列に入ったリスト、打ち間違いで重複したメールアドレス。すべてがそろっています。

最初の三つの正規形を、実践的に。1NF:アトミックな値 - product_names = 'keyboard,mouse'はこれに違反し、商品に対するWHEREJOINを実用的でなくします。2NF(複合キー):すべての列がキー全体に依存する - (注文, 商品)で識別される注文行に商品のカタログ価格を保存するのはこれに違反します。価格は商品だけに依存するからです。3NF:キー以外の列が別のキー以外の列に依存しない - customer_nameに依存するcustomer_emailはこれに違反します。だからこそalice@mail.coという重複が生まれるのです。これが修正したスキーマです。

SQL
CREATE TABLE customers (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  email TEXT NOT NULL UNIQUE
);

CREATE TABLE products (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  price REAL NOT NULL
);

CREATE TABLE purchases (
  id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL
    REFERENCES customers(id)
);

CREATE TABLE purchase_items (
  purchase_id INTEGER NOT NULL
    REFERENCES purchases(id),
  product_id INTEGER NOT NULL
    REFERENCES products(id),
  quantity INTEGER NOT NULL,
  PRIMARY KEY (purchase_id, product_id)
);
各事実は一つの場所に存在し、外部キーがそれらすべてを結びつける。
SQL
PRAGMA foreign_keys = ON;

INSERT INTO customers (name, email)
VALUES ('Alice', 'alice@mail.com');

INSERT INTO products (name, price)
VALUES ('keyboard', 49.0), ('mouse', 25.0);

INSERT INTO purchases (customer_id) VALUES (1);

INSERT INTO purchase_items
  (purchase_id, product_id, quantity)
VALUES (1, 1, 1), (1, 2, 1);

SELECT c.name, p.name AS product,
       i.quantity, p.price
FROM purchase_items AS i
JOIN purchases AS pu ON pu.id = i.purchase_id
JOIN customers AS c ON c.id = pu.customer_id
JOIN products AS p ON p.id = i.product_id;
完全な詳細は結合によって再構築される。

では、いつ非正規化すべきでしょうか? 読み取りが、計測されたボトルネックになったときです。重要な画面での五つのテーブルの結合、レンダリングのたびに再計算される集約。誠実なパターンは次のとおりです。キャッシュ列(注文時に固定されるpurchases.total - これは正当です。支払われた価格は歴史的な事実であり、カタログ価格とは別物です)、トリガーによって維持されるカウンター、あるいはバッチで再構築されるレポート用テーブル。支払う代償は、各コピーがずれうること、そして一貫性を保証するのがエンジンではなく、いまやあなたのコードだということです。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. 'keyboard,mouse'を保持するproduct_names列は、どの正規形に違反しますか?
    • 1NF
    • 2NF
    • 3NF
  2. 3NF違反を示す症状はどれですか?
    • 二つの列からなる主キー
    • 別のキー以外の列に依存するキー以外の列
    • 二次インデックスのないテーブル
  3. 非正規化はいつ正当化されますか?
    • 設計段階から、将来の結合を節約するために
    • 計測のあと、重要な読み取りに対して、不整合のリスクを受け入れて
    • 決して。スキーマは何があっても3NFのままでなければならない