カーディナリティ
線の形を決める
前回、テーブルのあいだに線を引きました。しかし線を引くだけでは足りません。その線が何対何なのかを決める必要があります。これをカーディナリティ、日本語では多重度と呼びます。
形は3つしかありません。
| 形 | 例 |
|---|---|
| 1対1 | 注文と配送伝票 |
| 1対多 | 店舗と注文 |
| 多対多 | 注文と商品 |
両方向から聞く
多重度を決めるときは、必ず両方向から質問します。片方向だけ考えると必ず間違えます。
店舗と注文なら、次の2つを聞きます。
- 1つの店舗に、注文はいくつ紐づくか → 0件以上
- 1件の注文に、店舗はいくつ紐づくか → ちょうど1つ
片方が「多」、もう片方が「1」なので 1対多です。もし両方が「多」なら多対多、両方が「1」なら1対1になります。
思い込みで決めない
ここが設計で一番間違えやすいところです。「顧客1人につきメールアドレスは1つ」と決めてしまってから、あとで「会社用と個人用を登録したい」と言われて作り直す。よくある失敗です。
思い込みではなく、要件と実データで裏を取ります。 すでにデータがあるなら、数えてみるのが一番早いです。
SELECT customer_id, count(*) FROM orders GROUP BY customer_id;これで最大が1なら、いまのところ1対1に見えます。2以上が1件でもあれば、それは確実に1対多です。ただし最大が1でも1対1とは限りません。たまたま今そうなっているだけかもしれないからです。数えて多が出たら確定、出なかったら要件に戻って確認する、という使い方をします。
0件を許すかどうか
もうひとつ決めることがあります。「0件を許すか」です。
注文が1件も無い店舗はあり得ます。開店したばかりの店です。だから stores ||--o{ orders と書きます。o は0以上を意味します。
一方、明細が1件も無い注文はあり得ません。何も買っていない注文は注文ではないからです。これは orders ||--|{ order_items で、|{ は1以上を意味します。
この違いは、あとで「0件でも成立するのか」を判断するときに効いてきます。
手を動かす
実際の注文データを数えて、店舗と注文の多重度を確かめます。
テーブル構造
CREATE TABLE stores (
id integer PRIMARY KEY,
name text NOT NULL,
pref text NOT NULL
);
CREATE TABLE customers (
id integer PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE orders (
id integer PRIMARY KEY,
store_id integer NOT NULL REFERENCES stores (id),
customer_id integer NOT NULL REFERENCES customers (id)
);
INSERT INTO stores VALUES
(1, '渋谷店', '東京都'),
(2, '梅田店', '大阪府'),
(3, '博多店', '福岡県');
INSERT INTO customers VALUES
(1, '田中あかり'),
(2, '佐藤けんじ'),
(3, '鈴木みか');
INSERT INTO orders VALUES
(1, 1, 1),
(2, 1, 2),
(3, 1, 1),
(4, 2, 3);
期待される出力
| id | name | order_count |
|---|---|---|
| 1 | 渋谷店 | 3 |
| 2 | 梅田店 | 1 |
| 3 | 博多店 | 0 |