SQL入門 STEP11|主キーと外部キーを学ぼう

karin@taishi@felmia

テーブル同士をつなぐ仕組みを知ろう

ここまでは、主にproductsという1つのテーブルを使ってSQLを学んできました。

現在のproductsには、商品名や価格、カテゴリーが入っています。

product_id | product_name    | price  | category
-----------+-----------------+--------+-----------
1          | ノートPC         | 120000 | PC
2          | マウス           | 3000   | 周辺機器
3          | キーボード        | 8000   | 周辺機器
4          | モニター          | 30000  | 周辺機器
5          | USBメモリ        | 2000   | アクセサリ
6          | Webカメラ        | 7000   | 周辺機器
7          | ヘッドセット      | 7000   | 周辺機器
8          | HDMIケーブル 2m   | 1800   | アクセサリ

このくらいのデータなら、1つのテーブルでもそれほど困りません。

ですが、商品が1000件、10000件と増えていったらどうでしょう。

PCや周辺機器というカテゴリー名を、商品ごとに何度も保存することになります。

さらに、

「周辺機器という名前をPC周辺機器に変更したい」

となった場合、同じカテゴリーを使っている商品をすべて変更しなければなりません。

そこで、カテゴリーを別のテーブルに分けて管理する方法を考えてみます。

カテゴリーを別のテーブルにしてみよう

例えば、カテゴリーだけを管理するcategoriesテーブルを作ります。

イメージは次のようになります。

category_id | category_name
------------+---------------
1           | PC
2           | 周辺機器
3           | アクセサリ

それぞれのカテゴリーに、category_idという番号を付けています。

そして商品のほうには、

product_id | product_name    | price  | category_id
-----------+-----------------+--------+------------
1          | ノートPC         | 120000 | 1
2          | マウス           | 3000   | 2
3          | キーボード        | 8000   | 2
4          | モニター          | 30000  | 2
5          | USBメモリ        | 2000   | 3

のように、カテゴリー名ではなくcategory_idを持たせます。

商品ID2のマウスを見ると、category_idは2です。

categoriesテーブルでID2を確認すると、

2 | 周辺機器

となっています。

つまり、

マウスは「周辺機器」というカテゴリーに属している

と判断できます。

ルミィ
ルミィ

同じ「2」を使って、商品とカテゴリーがつながっているんだね♪

主キーをもう一度確認しよう

ここで、STEP2に登場したPRIMARY KEYを思い出してみましょう。

productsテーブルを作ったときは、

product_id INTEGER PRIMARY KEY

としました。

PRIMARY KEYは、日本語では主キーと呼ばれます。

主キーには、そのデータを特定するための値を指定します。

例えば、

product_id
----------
1
2
3
4
5

という商品IDがあれば、

「商品IDが3の商品」

と指定するだけで、1つの商品を特定できます。

同じ商品IDが何件もあったら、どの商品なのか分からなくなってしまいます。

そのため、主キーには同じ値を重複して登録できません。

主キー(PRIMARY KEY)

テーブルの中から、1つのデータを特定するために使います。

今回なら、

products   → product_id
categories → category_id

を主キーとして使います。

主キーには同じ値を重複して登録できません。

categoriesテーブルを作ろう

それでは実際にcategoriesテーブルを作ります。

CREATE TABLE categories (
    category_id INTEGER PRIMARY KEY,
    category_name VARCHAR(50)
);

ここでもcategory_idにPRIMARY KEYを設定しています。

作成できたら、カテゴリーを登録しましょう。

INSERT INTO categories (category_id, category_name)
VALUES
    (1, 'PC'),
    (2, '周辺機器'),
    (3, 'アクセサリ');

登録できたかSELECTで確認します。

SELECT *
FROM categories
ORDER BY category_id ASC;

次の3件が表示されればOKです。

category_id | category_name
------------+---------------
1           | PC
2           | 周辺機器
3           | アクセサリ

外部キーとは?

ここからが今回の新しい内容です。

productsとcategoriesをつなぐために使うのが外部キー(FOREIGN KEY)です。

先ほどの商品データでは、

product_id | product_name | category_id
-----------+--------------+------------
1          | ノートPC      | 1
2          | マウス        | 2

となります。

このように、別のテーブルにあるデータとの関係を持たせるために使うカラムが外部キーです。

外部キー(FOREIGN KEY)

別のテーブルにあるデータとの関係を持たせるために使います。

今回なら、

products.category_id
        ↓
categories.category_id

という関係を作ります。

商品が「どのカテゴリーに属しているのか」を、category_idを使って表せるようになります。

productsにcategory_idを追加しよう

現在のproductsには、STEP10で作ったcategoryがあります。

今回はテーブル同士をつなぐため、新しくcategory_idを追加します。

ALTER TABLE products
ADD COLUMN category_id INTEGER;

これで、

product_id
product_name
price
category
category_id

という状態になります。

まだcategory_idには値が入っていません。

そこで、商品ごとにカテゴリーIDを設定します。

商品にカテゴリーIDを設定しよう

STEP10で使ったUPDATEをもう一度使います。

UPDATE products
SET category_id = 1
WHERE product_id = 1;

UPDATE products
SET category_id = 2
WHERE product_id IN (2, 3, 4, 6, 7);

UPDATE products
SET category_id = 3
WHERE product_id IN (5, 8);

これで、

  • ノートPC → 1(PC)
  • マウスなど → 2(周辺機器)
  • USBメモリなど → 3(アクセサリ)

という関係ができました。

確認してみましょう。

SELECT
    product_id,
    product_name,
    category_id
FROM products
ORDER BY product_id ASC;
product_id | product_name    | category_id
-----------+-----------------+------------
1          | ノートPC         | 1
2          | マウス           | 2
3          | キーボード        | 2
4          | モニター          | 2
5          | USBメモリ        | 3
6          | Webカメラ        | 2
7          | ヘッドセット      | 2
8          | HDMIケーブル 2m   | 3

外部キーを設定しよう

category_idを追加しただけでも、番号を使ってカテゴリーとの関係を表すことはできます。

ただし、今の状態では存在しないカテゴリーIDを登録することもできてしまいます。

そこでFOREIGN KEYを設定します。

ALTER TABLE products
ADD CONSTRAINT fk_products_category
FOREIGN KEY (category_id)
REFERENCES categories(category_id);

少し長くなりましたね。

今回は全部を暗記する必要はありません。

特に見てほしいのはここです。

FOREIGN KEY (category_id)
REFERENCES categories(category_id)

これは、

productsのcategory_idは、categoriesのcategory_idを参照します

という指定です。

ADD CONSTRAINT fk_products_categoryの部分は、この制約にfk_products_categoryという名前を付けています。

ルミィ
ルミィ

長く見えるけど、「どのカラムが、どこを参照するの?」を見ると分かりやすいね♪

存在しないカテゴリーを設定するとどうなる?

外部キーを設定したことで、products.category_idには何でも好きな番号を入れられるわけではなくなりました。

現在、categoriesにあるIDは、

1
2
3

です。

では、存在しない99を設定しようとするとどうなるでしょうか。

次のSQLは、エラーを確認するためのものです。

UPDATE products
SET category_id = 99
WHERE product_id = 1;

実行すると、外部キーの制約に違反するためエラーになります。

categoriesにID99が存在しないからです。

エラーになったので、商品ID1のcategory_idは変更されません。

外部キーが間違った関係を防いでくれる

例えば、

商品 → category_id = 99

となっているのに、カテゴリーID99がどこにも存在しなければ、どのカテゴリーの商品なのか分かりません。

FOREIGN KEYを設定しておけば、このような存在しないカテゴリーとの関係を防ぐことができます。

主キーと外部キーの違いを整理しよう

ここは最初のうちは混ざりやすいので、一度整理しておきます。

種類今回の例役割
主キーcategories.category_idカテゴリーを特定する
外部キーproducts.category_idカテゴリーを参照する

例えば、

categories
category_id = 2  ← 主キー
      ↑
      │ 参照
      │
products
category_id = 2  ← 外部キー

という関係です。

categories側の2は、

「カテゴリーID 2のデータを特定する番号」

です。

products側の2は、

「カテゴリーID 2を参照しています」

という意味になります。

同じcategory_idという名前でも、テーブルによって役割が違うところに注意してください。

なぜカテゴリー名ではなくIDでつなぐの?

ここで、

「最初からcategoryに『周辺機器』と入っていたのだから、それでよくない?」

と思うかもしれません。

小さなデータなら、それでも扱えます。

ですが、例えば1000個の商品がすべて、

周辺機器

という文字を直接持っていたとします。

後からカテゴリー名を、

周辺機器
↓
PC周辺機器

に変える場合、多くの商品データを変更することになります。

カテゴリーを別テーブルにしてIDでつないでおけば、カテゴリー名そのものはcategories側で管理できます。

商品のほうは、

category_id = 2

という関係を持っているだけです。

こうして役割ごとにテーブルを分けて管理できるのが、リレーショナルデータベースの特徴の一つです。

ここでは難しい設計ルールまで覚えなくて大丈夫です。

「同じ情報を何度も持たせるのではなく、別テーブルに分けてIDでつなぐことがある」

くらいの理解から始めましょう。

STEP10のcategoryはどうする?

現在のproductsには、

category
category_id

の両方があります。

STEP10ではGROUP BYの練習をするために、カテゴリー名をproductsへ直接追加しました。

これからはcategoriesテーブルとcategory_idを使うので、元のcategoryは不要になります。

削除しておきましょう。

ALTER TABLE products
DROP COLUMN category;

これでproductsは、

product_id
product_name
price
category_id

という構成になります。

最後に2つのテーブルを確認しよう

まずproductsです。

SELECT *
FROM products
ORDER BY product_id ASC;
product_id | product_name    | price  | category_id
-----------+-----------------+--------+------------
1          | ノートPC         | 120000 | 1
2          | マウス           | 3000   | 2
3          | キーボード        | 8000   | 2
4          | モニター          | 30000  | 2
5          | USBメモリ        | 2000   | 3
6          | Webカメラ        | 7000   | 2
7          | ヘッドセット      | 7000   | 2
8          | HDMIケーブル 2m   | 1800   | 3

次にcategoriesです。

SELECT *
FROM categories
ORDER BY category_id ASC;
category_id | category_name
------------+---------------
1           | PC
2           | 周辺機器
3           | アクセサリ

この2つのテーブルは、category_idで関係しています。

ところが、今のSELECTではそれぞれ別々に表示しています。

できれば、

ノートPC    | 120000 | PC
マウス      | 3000   | 周辺機器
USBメモリ   | 2000   | アクセサリ

のように、商品とカテゴリー名を一緒に表示したいですよね。

そこで次に使うのがJOINです。

よくある間違い

主キーと外部キーを同じものだと思ってしまう

どちらにもcategory_idを使っているので、最初は混同しやすいところです。

categories.category_idは、カテゴリーそのものを特定する主キー。

products.category_idは、そのカテゴリーを参照する外部キーです。

役割で分けて考えてみましょう。

存在しないIDを外部キーに設定する

categoriesに、

1
2
3

しかない状態で、products.category_idへ99を設定することはできません。

FOREIGN KEYが、存在しないカテゴリーとの関係を防いでくれます。

IDだけ見て意味がないと思ってしまう

productsだけを見ると、

category_id = 2

では、何のカテゴリーなのか分かりにくいですね。

ですがcategoriesを見ると、

2 | 周辺機器

と分かります。

この2つのテーブルをまとめて取得する方法を、次のSTEPで学びます。

STEP11のまとめ

STEP11のポイント

  • データは役割ごとに複数のテーブルへ分けて管理することがある
  • PRIMARY KEYは1つのデータを特定するための主キー
  • 主キーには同じ値を重複して登録できない
  • FOREIGN KEYは別のテーブルとの関係を持たせるための外部キー
  • REFERENCESで参照するテーブルとカラムを指定する
  • 外部キーを設定すると、存在しないデータとの関係を防げる
  • 今回はcategory_idを使ってproductsとcategoriesをつないだ

今回は、これまでのように「データを取得するSQL」よりも、テーブル同士の関係について考える内容が中心でした。

主キーと外部キーは、名前だけで覚えようとすると分かりにくいかもしれません。

そんなときは、

主キー → そのデータを特定する

外部キー → 別のテーブルのデータを参照する

と考えてみてください。

そして今回作った、

products.category_id
        ↓
categories.category_id

という関係は、このまま次のSTEPで使います。

ルミィ
ルミィ

STEP11クリア!
2つのテーブルがcategory_idでつながったね。次はいよいよ、この2つを一緒に見てみよう♪

次のSTEPへ

productsには商品名や価格があり、categoriesにはカテゴリー名があります。

今は別々にSELECTしないと、それぞれの内容を見ることができません。

次はINNER JOINを使って2つのテーブルを結合し、

「商品名・価格・カテゴリー名」

を1回のSELECTで取得してみます。

ここまで作ってきた主キーと外部キーが、実際にどう使われるのかが見えてきます。

→ STEP12「INNER JOINでテーブルを結合しよう」

学習内容を確認したいときは、SQL入門の一覧から各STEPを確認できます。

→ SQL入門|初心者が基礎から学べるSQL学習ガイド

記事URLをコピーしました