第2正規形 (2NF)
商品名を直したいだけなのに、明細 800 行を書き換える
注文明細を 1 枚の表にまとめました。主キーは「どの注文の、どの商品か」の 2 列の組です。
SQL クエリ
CREATE TABLE order_items_bad (
order_id INT,
product_id INT,
product_name VARCHAR(100),
unit_price INT,
quantity INT,
PRIMARY KEY (order_id, product_id)
);商品名を「ノート」から「A4 ノート」に変えることになりました。この商品は 800 件の注文に登場しています。書き換える行は 800 行です。
1 行でも漏れると、商品別の集計に「ノート」と「A4 ノート」が別の商品として 2 行並びます。価格改定でも同じことが起きて、古い単価の行と新しい単価の行が混ざり、売上の合計が誰にも説明できない数字になります。
主キーの一部だけで決まる列を探す
なぜ 800 行に散らばったのかを、主キーとの関係から見ます。この表の主キーは (order_id, product_id) の 2 列です。列を 1 本ずつ点検します。
quantity— どの注文の、どの商品かが両方分かって初めて決まるproduct_name—product_idだけで決まる。order_idは関係ないunit_price— 同じくproduct_idだけで決まる
product_id が決まれば product_name も決まる、という関係を product_id → product_name と書き、関数従属と呼びます。そのうち、主キーの一部だけで決まっているものが部分関数従属です。product_id は主キー 2 列のうちの 1 列なので、product_name と unit_price がこれに当たります。
主キー全体が要る列と、一部で足りる列を同じ表に同居させると、一部で足りる列は組み合わせの数だけ複製されます。800 行の重複は、これが原因です。
決まる側の表へ引っ越させる
product_id だけで決まる列は、product_id が主キーになっている表へ移します。
SQL クエリ
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100),
unit_price INT
);
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_id)
);商品名の変更も価格改定も、products の 1 行を書き換えるだけになります。明細側に残るのは、その注文でだけ決まる quantity です。読むときに商品名や単価が要れば、product_id を手がかりに商品の表から取ってきます。
第 2 正規形とは、主キーの一部だけで決まる列が残っていない状態のことです。この問題は複合主キーの表でしか起きません。主キーが id 1 列の表には「一部」というものが存在しないので、点検の対象になるのは主キーが 2 列以上ある表だけです。
テーブル構造
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100),
unit_price INT
);
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_id)
);
INSERT INTO products VALUES
(10, 'ノート', 300),
(11, 'ペン', 150),
(12, '消しゴム', 100);
INSERT INTO order_items VALUES
(1001, 10, 2),
(1001, 11, 4),
(1002, 10, 1),
(1003, 12, 3),
(1004, 11, 5),
(1005, 10, 6);期待される出力
| name | total_amount |
|---|---|
| ノート | 2700 |
| ペン | 1350 |
| 消しゴム | 300 |