CS50でお馴染みのマスコットキャラクター、Duckが盗まれた!泥棒が誰で、どこへ逃げたのか、また誰が助けたのかを探し出せ!というのが今回の課題。めちゃくちゃ面白かった。手掛かりは、目撃情報、銀行取引、電話履歴、インタビュー、銀行口座、空港、フライト情報、などのデータを使って順番に話をつなげていく。
与えられたテーブルの種類
sqlite> .tables
airports crime_scene_reports people
atm_transactions flights phone_calls
bank_accounts interviews
courthouse_security_logs passengers
データベースの構造
まずは、各テーブルがどのようなデータ構造になっているのかを把握することから始める。どんなデータがどのような構造で保存されているのか、各テーブルの連携をざっと把握する必要がある。
-- Explore table schemas to understand what data is available and how tables connect oone another
sqlite> .schema airports
CREATE TABLE airports (
id INTEGER,
abbreviation TEXT,
full_name TEXT,
city TEXT,
PRIMARY KEY(id)
);
sqlite> .schema crime_scene_reports
CREATE TABLE crime_scene_reports (
id INTEGER,
year INTEGER,
month INTEGER,
day INTEGER,
street TEXT,
description TEXT,
PRIMARY KEY(id)
);
sqlite> .schema people
CREATE TABLE people (
id INTEGER,
name TEXT,
phone_number TEXT,
passport_number INTEGER,
license_plate TEXT,
PRIMARY KEY(id)
);
sqlite> .schema atm_transactions
CREATE TABLE atm_transactions (
id INTEGER,
account_number INTEGER,
year INTEGER,
month INTEGER,
day INTEGER,
atm_location TEXT,
transaction_type TEXT,
amount INTEGER,
PRIMARY KEY(id)
);
sqlite> .schema flights
CREATE TABLE flights (
id INTEGER,
origin_airport_id INTEGER,
destination_airport_id INTEGER,
year INTEGER,
month INTEGER,
day INTEGER,
hour INTEGER,
minute INTEGER,
PRIMARY KEY(id),
FOREIGN KEY(origin_airport_id) REFERENCES airports(id),
FOREIGN KEY(destination_airport_id) REFERENCES airports(id)
);
sqlite> .schema phone_calls
CREATE TABLE phone_calls (
id INTEGER,
caller TEXT,
receiver TEXT,
year INTEGER,
month INTEGER,
day INTEGER,
duration INTEGER,
PRIMARY KEY(id)
);
sqlite> .schema bank_accounts
CREATE TABLE bank_accounts (
account_number INTEGER,
person_id INTEGER,
creation_year INTEGER,
FOREIGN KEY(person_id) REFERENCES people(id)
);
sqlite> .schema interviews
CREATE TABLE interviews (
id INTEGER,
name TEXT,
year INTEGER,
month INTEGER,
day INTEGER,
transcript TEXT,
PRIMARY KEY(id)
);
sqlite> .schema courthouse_security_logs
CREATE TABLE courthouse_security_logs (
id INTEGER,
year INTEGER,
month INTEGER,
day INTEGER,
hour INTEGER,
minute INTEGER,
activity TEXT,
license_plate TEXT,
PRIMARY KEY(id)
);
sqlite> .schema passengers
CREATE TABLE passengers (
flight_id INTEGER,
passport_number INTEGER,
seat TEXT,
FOREIGN KEY(flight_id) REFERENCES flights(id)
);
まずは、目撃証言から…
まずは、目撃証言から次の手掛かりを探す
sqlite> SELECT description
FROM crime_scene_reports
WHERE month = 7
AND day = 28
AND street = "Chamberlin Street";
description
Theft of the CS50 duck took place at 10:15am at the Chamberlin Street courthouse. Interviews were conducted today with three witnesses who were present at the time — each of their interview transcripts mentions the courthouse.
手掛かりが更に増えていく
-- key information, searching by the key word 'courthouse'
sqlite> SELECT * FROM interviews WHERE year = 2020 AND month = 7 AND day = 28 AND transcript LIKE '%courthouse%';
id | name | year | month | day | transcript
161 | Ruth | 2020 | 7 | 28 | Sometime within ten minutes of the theft, I saw the thief get into a car in the courthouse parking lot and drive away. If you have security footage from the courthouse parking lot, you might want to look for cars that left the parking lot in that time frame.
162 | Eugene | 2020 | 7 | 28 | I don't know the thief's name, but it was someone I recognized. Earlier this morning, before I arrived at the courthouse, I was walking by the ATM on Fifer Street and saw the thief there withdrawing some money.
163 | Raymond | 2020 | 7 | 28 | As the thief was leaving the courthouse, they called someone who talked to them for less than a minute. In the call, I heard the thief say that they were planning to take the earliest flight out of Fiftyville tomorrow. The thief then asked the person on the other end of the phone to purchase the flight ticket.
SQL構文を整理する
というように順番にストーリーを繋げていき、最後は一つの構文で書くことができる。
まずは、泥棒本人。
-- Get the name of the thief
SELECT name FROM people
-- Query security logs
WHERE people.license_plate IN (
-- Get the license plates from the courthouse logs
SELECT license_plate FROM courthouse_security_logs
-- In the ten minute time frame (10:15 - 10:25)
WHERE year = 2020 AND month = 7 AND day = 28 AND hour = 10 AND minute > 15 AND minute < 25
)
-- Query ATM transactions
AND people.id IN (
-- Get person_id from the ATM transactions
SELECT person_id FROM bank_accounts
-- Let's join the bank account information so that we can grab the person_id
JOIN atm_transactions ON atm_transactions.account_number = bank_accounts.account_number
-- Transaction was on the day of the crime
WHERE atm_transactions.year = 2020 AND atm_transactions.month = 7 AND atm_transactions.day = 28
-- It was a withdrawal
AND transaction_type = "withdraw"
-- It occured on Fifer Street
AND atm_transactions.atm_location = "Fifer Street"
)
-- Query calls
AND people.phone_number IN (
-- Get the phone numbers from calls
SELECT caller FROM phone_calls
-- Date of the crime
WHERE year = 2020 AND month = 7 AND day = 28
-- Duration less than a minute
AND duration < 60
)
-- Query first flight passenger list
AND people.passport_number IN (
-- Get the passport numbers of passengers
SELECT passport_number FROM passengers
-- On the first flight
WHERE flight_id IN (
-- Get the id of the first flight of the next day
SELECT id FROM flights WHERE year = 2020 AND month = 7 AND day = 29
ORDER BY hour, minute ASC LIMIT 1
)
);
次に、どこへ逃げたのか。空港とフライト情報から辿る。
-- Get the city name
SELECT city FROM airports
-- From the first flight of the day
WHERE id IN (
SELECT destination_airport_id FROM flights WHERE year = 2020 AND month = 7 AND day = 29
ORDER BY hour, minute ASC LIMIT 1
);
最後に、泥棒を助けた人を通話履歴から辿る。
-- Get the accomplice's name
SELECT name FROM people
-- Using their phone number
WHERE phone_number IN (
-- From the list of phone calls
SELECT receiver FROM phone_calls
-- On the date of the crime
WHERE year = 2020 AND month = 7 AND day = 28
-- And where the caller was our criminal
AND caller = (
-- Ernest is a prick
SELECT phone_number FROM people WHERE name = "Ernest"
)
-- And to reduce the likelihood of getting more than one result, let's constrain it a little more
AND duration < 60
);
結果
結果は、次の通り。
The THIEF is: Ernest
The thief ESCAPED TO: London
The ACCOMPLICE is: Berthold
未希 諒
香港在住の34歳サラリーマン。戦略家、写真家、旅人、サウナー。 2025年までに季節を選びながら世界中を飛び回る仕事をするために準備中。その道中で発見した気づきや気になる話題を中心に発信しています。2010年より旅をスタートし通算500箇所の宿に宿泊。年間の3分の1をホテルで暮らす。
Related posts
About media
Ramps Blog は、ノマドによるノマドのためのブログです。日本、香港、シンガポール、タイ、台湾のノマド生活を通じて見えた景色、特に食、カルチャー、テクノロジー、ビジネス、アート、デザインなどについて発信しています。
Recent Posts
Twitter feed is not available at the moment.