テーブル構造イメージ
まずはテーブル同士の関係を思い出すところから始めます。今回の問題では、memory(記憶)、person(人物)、そしてcategory(カテゴリ)の3つのテーブルが存在します。今回のクエリで直接使うのはmemoryとcategoryの2つです。下記のようなER図からテーブル構造をざっくりイメージできます。
上の図を確認すると、memoryテーブルはcategory_idを持ち、そこからcategoryテーブルのidと結びついていることがわかります。
-
memory テーブル
- id (int)
- talk (text)
- importance (int)
- person_id (int)
- category_id (int)
- created_at (date)
-
category テーブル
- id (int)
- name (text)
取り出したいデータと条件
問題文では、「memory.importance >= 3
かつ category.name = '悲しみ'
」を満たすmemoryレコードのidとtalkを取得するように求められています。つまり、重要度が 3 以上で、**カテゴリ名が「悲しみ」**に属する会話内容を取り出したい、というわけですね。
ここでポイントになるのが、memoryテーブルだけではカテゴリ名はわからないということ。カテゴリ名はcategoryテーブルに格納されているので、category_idを使って2つのテーブルをつなげる必要があります。
INNER JOIN のおさらい
ここで登場するのがJOIN(内部結合)です。
SELECT memory.id, memory.talk
FROM memory
JOIN category ON memory.category_id = category.id
WHERE memory.importance >= 3
AND category.name = '悲しみ';
-
JOIN category ON memory.category_id = category.id
memoryテーブルのcategory_id
とcategoryテーブルのid
が一致するレコード同士を結びつけ、両方にデータがある行だけを返します(INNER JOIN)。 -
WHERE memory.importance >= 3
memoryテーブルのimportance
が3以上の行に絞り込みます。 -
AND category.name = '悲しみ'
さらに、結合されたcategoryテーブルのname
が「悲しみ」となっている行のみに限定します。
これによって、「悲しみ」カテゴリに属し、かつ重要度が3以上の会話履歴だけを抽出することができます。
なぜJOINが必要か
もしmemoryテーブルにカテゴリ名そのものが保存されていれば、わざわざ別テーブルをJOINする必要はありません。しかし、本来のデータ設計ではカテゴリー名を別テーブルに分けて管理します。これは正規化の考え方に沿っています。
JOINは、こうした分割されたテーブル同士のデータを組み合わせるために不可欠な機能です。久しぶりにSQLに触れると、この結合条件(ON memory.category_id = category.id
など)を忘れがちなので、ぜひ思い出しておきます。
まとめ
- 重要度が3以上&カテゴリ名が「悲しみ」という2つの条件を満たすmemoryレコードを取得したい。
- カテゴリ名はcategoryテーブルに格納されているため、JOINでテーブルを結合し、WHERE句で条件を指定する。
- SELECTでは最終的に取得したいカラム(この場合は
memory.id
とmemory.talk
)を列挙する。
この記事を読んで、「ああ、JOINとWHERE句でテーブル同士を結びつけて条件をかけるのは、こうだったな」と思い出していただけたら幸いです。