カーディナリティ

線の形を決める

前回、テーブルのあいだに線を引きました。しかし線を引くだけでは足りません。その線が何対何なのかを決める必要があります。これをカーディナリティ、日本語では多重度と呼びます。

形は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);

期待される出力

idnameorder_count
1渋谷店3
2梅田店1
3博多店0

ヒント

query.sql
学習モード
コードの実行結果
データベースを初期化中...