はじめに
みなさん、こんにちは!この記事では「SQLって実はそこまで難しくないんだよ」ということをテーマに、問題「巨大コーポの最上階」の解き方を詳しく解説してみたいと思います。今回の問題では5つのテーブル(person, battle, memory, log, category)を使い、ちょっとだけ複雑に見える条件式も登場しますが、段階を踏んで理解していけば大丈夫。初心者の方もぜひ気軽にご覧ください!
問題の概要
問題では、person
テーブルのうち重要度 (importance
) が5の人物を対象に、その人物が削除された(あるいは亡くなった)日と同じ日時に行われた戦闘の記録から、対応する記憶や会話内容を取り出す…という内容になっています。
具体的には、person.deleted_at
と同じ日付の battle.created_at
を持つ戦闘記録と、そこから紐づく memory.id
, memory.talk
(会話内容)、そしてその人物の person.name
、さらにその戦闘の battle.created_at
を取得する、というのがゴールです。
テーブルの構造
まずは各テーブルの構造を確認しましょう。関係性を視覚的に示した図はこちらです。
上の図を見ると、以下の5つのテーブルがあります。
-
person (人物)
-
id
(主キー) -
name
(人物名) -
biography
(詳細) -
importance
(重要度) -
created_at
(出会った日) -
deleted_at
(亡くなった日など)
-
-
memory (記憶)
-
id
(主キー) -
talk
(会話内容) -
importance
(重要度) -
person_id
(人物ID) -
category_id
(カテゴリID) -
created_at
(記録日)
-
-
battle (戦闘)
-
id
(主キー) -
person_id
(人物ID) -
importance
(重要度) -
result
(結果) -
created_at
(記録日)
-
-
log (ログ)
-
person_id
(人物ID) -
memory_id
(記憶ID) -
created_at
(記録日)
-
-
category (カテゴリ)
-
id
(主キー) -
name
(記憶カテゴリ名)
-
「どのカラムがどのテーブルにあるのか」というイメージをしっかり持っておくと、SQL文の組み立てがとてもスムーズになります。
必要となるデータの流れを整理する
今回の問題は、
-
person
テーブルから条件を絞る(importance = 5
) -
person.deleted_at
とbattle.created_at
が同じ日付であるbattle
を探す -
battle.person_id
からlog
テーブルとmemory
テーブルに紐づける - 最終的に
memory.id
,memory.talk
,person.name
,battle.created_at
を取得する
という流れでデータを取得します。
そこで重要なポイントがいくつかあります。
-
JOINの順序
どのテーブルとどのテーブルを JOIN するかを把握する。-
person
とbattle
はperson.id = battle.person_id
-
battle
とlog
はbattle.person_id = log.person_id
-
log
とmemory
はlog.memory_id = memory.id
-
-
WHERE 句での条件指定
person.importance = 5
person.deleted_at = battle.created_at
ここさえ理解しておけば、SQL文はスムーズに作れます。
実際のSQL文
問題で提示されたSQL文は以下のとおりです。
SELECT
memory.id AS id,
memory.talk AS talk,
person.name AS name,
battle.created_at AS created_at
FROM
person
JOIN
battle ON person.id = battle.person_id
JOIN
log ON battle.person_id = log.person_id
JOIN
memory ON log.memory_id = memory.id
WHERE
person.importance = 5
AND person.deleted_at = battle.created_at;
SQL文の読み方
「SQLってなんだか小難しそう」という印象があるかもしれませんが、意外と英語の文章を読むような感覚で慣れれば大丈夫です。
-
SELECT:どの列を取り出すか
-
memory.id, memory.talk, person.name, battle.created_at
というように、取得したい列をリストアップしています。
-
-
FROM:どのテーブルを参照するか
- 最初に
person
と書かれています。ここを出発点に JOIN していきます。
- 最初に
-
JOIN … ON …:どのカラム同士を結びつけるか
-
JOIN battle ON person.id = battle.person_id
→person
テーブルとbattle
テーブルを紐づける -
JOIN log ON battle.person_id = log.person_id
→battle
テーブルとlog
テーブルを紐づける -
JOIN memory ON log.memory_id = memory.id
→log
テーブルとmemory
テーブルを紐づける
-
-
WHERE:最終的に欲しい行の条件
-
person.importance = 5
→ 重要度が5の人物 -
person.deleted_at = battle.created_at
→deleted_at
(消滅・死亡日など) とbattle.created_at
(戦闘が行われた日) が同じ
-
こうして見ると、「テーブル間を繋いで、条件を絞って、必要な列を取り出す」だけですよね。
どうやって解き方を考えるか
この問題を解く時に大切なのは、「レコード同士をどのように繋げたいか」を頭の中でイメージすることです。
- 今回は「同じ人物」に関する情報を統合したいので、
person_id
同士を基点にテーブルを結合します。 - さらに、「日にちが同じ」という特殊な条件をWHERE句に追加します。
多くのSQLの問題は「結合 (JOIN)」と「検索条件 (WHERE)」がポイント。慣れるまでは、紙にテーブルの構造を書き出して、どこを繋ぐのか矢印で描いてみるのも良い方法です。
補足コラム:SQLは英語っぽい
SQLは「Structured Query Language(構造化問い合わせ言語)」の略です。SELECT, FROM, WHERE, JOIN など英語に近い単語が並んでいるのは、データベースとのやり取りが英語圏をベースに設計されているからですね。
もしも「プログラミング言語としては英語が多くて抵抗がある…」と思っている方も、大丈夫。SQLの構文はパターン化されているので、慣れればそこまで苦労しません。英語として読むのではなく、「SELECT = 取り出したい列を指定する」「JOIN = テーブルをつなぐ」といった構文ルールとして覚えてしまうのも手ですよ。
問題解決の流れをおさらい
-
問題文を読む
「どのテーブルから何を取り出したいのか」「どういう条件があるのか」をまとめる。 -
テーブル構造を確認
カラム名やキー(主キー、外部キー)をしっかり把握する。 -
JOINの仕方とWHERE句の条件を整理
実際にテーブルを結合してみる。 -
必要な列をSELECTに追加
出力が揃うようにカラムを指定する。
この問題は少し条件が特殊(deleted_at
と battle.created_at
が等しい)ですが、慣れてしまえばそう難しい構文ではありません。
結論
最終的な答えは、問題文にあるとおり下記のSQL文でOKです。何かエラーが出る場合は、まずテーブル名やカラム名をタイプミスしていないかを確認するのが鉄則です。初心者のうちはスペルミスが多いので、実行前のチェックを習慣づけましょう!
SELECT
memory.id AS id,
memory.talk AS talk,
person.name AS name,
battle.created_at AS created_at
FROM
person
JOIN
battle ON person.id = battle.person_id
JOIN
log ON battle.person_id = log.person_id
JOIN
memory ON log.memory_id = memory.id
WHERE
person.importance = 5
AND person.deleted_at = battle.created_at;
さいごに
データベースやSQLに初めて触れる方にとっては、最初は「テーブルを繋げるってどういうこと?」「親テーブル?子テーブル?」と混乱することも多いでしょう。ですが、数をこなしていくうちに自然と図を思い浮かべながらクエリを書けるようになってきます。今回の問題で、SQLクエリの基本的な組み立てかたを体験していただけたなら幸いです。
これからもデータベースを扱う機会が増えるほど、「SQLを知っていてよかった!」と思う瞬間が必ず訪れます。ぜひ恐れずに実際に手を動かして、少しずつ慣れていってください。
それではここまで読んでいただき、ありがとうございました。皆さんのSQL学習がスムーズに進むことを祈っています!