5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【電脳少女プログラミング2088 ─壊レタ君を再構築─】 巨大コーポの最上階の問題をSQLで解いてみよう

Posted at

はじめに

みなさん、こんにちは!この記事では「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つのテーブルがあります。

  1. person (人物)

    • id (主キー)
    • name (人物名)
    • biography (詳細)
    • importance (重要度)
    • created_at (出会った日)
    • deleted_at (亡くなった日など)
  2. memory (記憶)

    • id (主キー)
    • talk (会話内容)
    • importance (重要度)
    • person_id (人物ID)
    • category_id (カテゴリID)
    • created_at (記録日)
  3. battle (戦闘)

    • id (主キー)
    • person_id (人物ID)
    • importance (重要度)
    • result (結果)
    • created_at (記録日)
  4. log (ログ)

    • person_id (人物ID)
    • memory_id (記憶ID)
    • created_at (記録日)
  5. category (カテゴリ)

    • id (主キー)
    • name (記憶カテゴリ名)

「どのカラムがどのテーブルにあるのか」というイメージをしっかり持っておくと、SQL文の組み立てがとてもスムーズになります。

必要となるデータの流れを整理する

今回の問題は、

  1. person テーブルから条件を絞る(importance = 5
  2. person.deleted_atbattle.created_at が同じ日付である battle を探す
  3. battle.person_id から log テーブルと memory テーブルに紐づける
  4. 最終的に memory.id, memory.talk, person.name, battle.created_at を取得する

という流れでデータを取得します。

そこで重要なポイントがいくつかあります。

  1. JOINの順序
    どのテーブルとどのテーブルを JOIN するかを把握する。

    • personbattleperson.id = battle.person_id
    • battlelogbattle.person_id = log.person_id
    • logmemorylog.memory_id = memory.id
  2. 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 = テーブルをつなぐ」といった構文ルールとして覚えてしまうのも手ですよ。

問題解決の流れをおさらい

  1. 問題文を読む
    「どのテーブルから何を取り出したいのか」「どういう条件があるのか」をまとめる。
  2. テーブル構造を確認
    カラム名やキー(主キー、外部キー)をしっかり把握する。
  3. JOINの仕方とWHERE句の条件を整理
    実際にテーブルを結合してみる。
  4. 必要な列をSELECTに追加
    出力が揃うようにカラムを指定する。

この問題は少し条件が特殊(deleted_atbattle.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学習がスムーズに進むことを祈っています!

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?