SQL games guide
SQL Murder Mystery Solution: Step-by-Step Walkthrough + Hints
This is the full SQL Murder Mystery solution: both names, every query with its real result, and a hint for each step if you want to solve the case yourself. The SQL Murder Mystery is a free browser game from the Knight Lab at Northwestern University. There was a murder in SQL City on January 15, 2018, and you find the killer with SQL queries on a police database at mystery.knightlab.com.
Spoiler: the answer to both parts is below. For hints only, go to Where are you stuck?
Who is the killer in the SQL Murder Mystery?
The killer is Jeremy Bowers, person 67318. The person who hired him is Miranda Priestly. In part 1 of the game you
find the killer, and in part 2 you find the person behind the murder. Check each name with the game's
solution table:
INSERT INTO solution VALUES (1, 'Jeremy Bowers');
SELECT value FROM solution; If you get "That's not the right person. Try again!" for one of these names, read why the game rejects a correct answer.
Why does the SQL Murder Mystery reject the correct answer?
If you found the right person and the SQL Murder Mystery still says "That's not the right person. Try again!", look at these four causes first.
-
You did
height IN (65, 67)You get One woman, Red Korb. Check her name, and the game replies "That's not the right person. Try again!"
Do this
height BETWEEN 65 AND 67The killer says that she is "around" 65 or 67 inches tall. With IN, you keep only those two numbers, and the woman who hired him is 66 inches tall. See Part 2, step 2.
-
You did
INSERT INTO solution VALUES (1, 'jeremy bowers');You get "That's not the right person. Try again!" You get the same reply for a space after the name, or for two spaces between the names.
Do this
Type the name exactly as the person table has it, with a capital letter on each name and no extra spaces.SQLite compares your text with the answer character by character, so a different letter case or one extra space is a different name.
-
You did
The answer check, in the sqlite3 command-line tool on your computerYou get An error that starts with no such column: "Congrats, you found the murderer!
Do this
.dbconfig dqs_dml onThe replies in the answer check are in double quotes. New versions of SQLite read text in double quotes as a column name, so you get the error even for the correct name. Run the setting once, then run the INSERT again. In the browser game and in Python, you do not need the setting. See how to play on your computer.
-
You did
A date filter in step 1 of Knight Lab's own walkthrough pageYou get The word "Incorrect", although your query finds the correct report.
Do this
Keep date = 20180115. One report is the correct result.There is no date filter in the "show solution" query on that page, so you get 3 murders in SQL City. Two of them are from February 15, 2018. On the same page, Knight Lab tells you to disregard the word "incorrect" if you find one report. See Part 1, step 1.
SQL from other answers that fails in the SQL Murder Mystery
Some walkthroughs and AI answers print SQL with a wrong column name or the wrong letter case. Here is what you get from the game's database for each one.
| You copied | You get | Write |
|---|---|---|
drivers_license.plate | Error: no such column | drivers_license.plate_number |
crime_type = 'murder' | Error: no such column | type = 'murder' |
membership_status = 'Gold' | 0 rows | membership_status = 'gold' |
Where are you stuck in the SQL Murder Mystery?
Find the line that matches what you have now in the SQL Murder Mystery, and go to that step.
| What you have now | Go to |
|---|---|
| You have the prompt, and you have not run a query yet. | Part 1, step 1: Find the crime scene report |
| A crime scene report that describes two witnesses. | Part 1, step 2: Find the first witness |
| The first witness: person 14887, at the last house on Northwestern Dr. | Part 1, step 3: Find the second witness |
| The ids of both witnesses, 14887 and 16371. | Part 1, step 4: Read the two interviews |
| Two interview transcripts: a gym bag, a 48Z membership, a car plate with H42W. | Part 1, step 5: Check the gym clue |
| Two gold 48Z gym members who were at the gym on January 9. | Part 1, step 6: Check the car plate |
| Three people with an H42W car plate. | Part 1, step 7: Put the clues in one query |
| The game replied "Congrats, you found the murderer! But wait, there's more..." | Part 2, step 1: Read the killer's interview |
| The killer's interview: a red-haired woman with a Tesla Model S. | Part 2, step 2: Find the women who match the description |
| Three red-haired women who drive a Tesla Model S. | Part 2, step 3: Find who went to the concert 3 times |
| Two people who went to the SQL Symphony Concert 3 times in December 2017. | Part 2, step 4: Put the clues in one query |
How do you play the SQL Murder Mystery?
You play the SQL Murder Mystery in your browser, with no account and no install. You play in five steps:
- Open mystery.knightlab.com and read the prompt: a murder, on January 15, 2018, in SQL City.
- Look at the tables. The game runs on SQLite, so
SELECT name FROM sqlite_master WHERE type = 'table'lists them. The diagram below shows how they link. - Write a query in one of the boxes and run it. Each result gives you a clue for the next query.
- Follow the clues through the tables until one person is left.
- Check your answer with the game's
solutiontable.
To check an answer, run these two statements with the name that you found:
INSERT INTO solution VALUES (1, 'Name Here');
SELECT value FROM solution; For a wrong name, you get the reply "That's not the right person. Try again!" For the killer's name, you get "Congrats, you found the murderer! But wait, there's more..." and that reply starts part 2.
What tables are in the SQL Murder Mystery database?
The SQL Murder Mystery database has 9 tables. Most of the case is about connecting them by their ids, so keep this diagram open while you play. The blue columns are the ones that you join on, with a line between each pair.
| Table | What it holds | Columns that link |
|---|---|---|
crime_scene_report | Police reports: date (a number such as 20180115), type of crime, a description and the city. | |
person | Every person: name, address and the ids that link to other tables. | id, license_id, ssn |
interview | What people told the police. | person_id |
drivers_license | Age, height, eye and hair color, gender, car and plate number. | id |
get_fit_now_member | Members of the Get Fit Now Gym and their membership status. | id (such as 48Z55), person_id |
get_fit_now_check_in | Each gym visit, with the date and times. | membership_id |
facebook_event_checkin | Each check-in at an event, with the event name and date. | person_id |
income | Annual income. | ssn |
solution | Where you put your answer to check it. |
The links between tables: person.license_id = drivers_license.id,
person.ssn = income.ssn, and person_id in the other tables =
person.id. A gym visit links to a member by membership_id = get_fit_now_member.id.
Why does my SQL Murder Mystery query return no rows?
You get no rows in the SQL Murder Mystery when a filter does not match the data exactly. Look at three things first: the dates are numbers such as 20180115, the = sign matches letter case exactly, and a LIKE pattern can be too narrow. Each item is a wrong turn, with what you get and what to write.
-
You typed
date = '2018-01-15'You get 0 rows
Write
date = 20180115The date column is a number, not a date, so text never matches it. See Part 1, step 1.
-
You typed
type = 'Murder' or city = 'sql city'You get 0 rows
Write
type = 'murder' AND city = 'SQL City'With =, the letter case must match the data exactly. See Part 1, step 1.
-
You typed
address_street_name = 'Northwestern Drive'You get 0 rows
Write
address_street_name = 'Northwestern Dr'The data uses the short form of the street name, as the report does. See Part 1, step 2.
-
You typed
ORDER BY address_number LIMIT 1You get the wrong person: Kinsey Erickson, house 309
Write
ORDER BY address_number DESC LIMIT 1Without DESC, the sort goes from low to high, so you get the first house, not the last. See Part 1, step 2.
-
You typed
name = 'Annabel'You get 0 rows
Write
name LIKE 'Annabel%'The name column holds full names, so the first name alone matches nobody. See Part 1, step 3.
-
You typed
check_in_date = '2018-01-09'You get 0 rows
Write
check_in_date = 20180109Gym dates are numbers too. See Part 1, step 5.
-
You typed
plate_number LIKE 'H42W%'You get one plate, H42W0X, and the driver is a woman
Write
plate_number LIKE '%H42W%'The witness said the plate includes H42W, not that it starts with H42W. The pattern without the first % finds only one plate, and it belongs to a woman. See Part 1, step 6.
-
You typed
hair_color = 'Red' or car_model = 'Tesla Model S'You get 0 rows
Write
hair_color = 'red' AND car_make = 'Tesla' AND car_model = 'Model S'Hair colors are lowercase, and the make and the model are two columns. See Part 2, step 2.
-
You typed
JOIN income ON income.ssn = person.ssnYou get 2 women, not 3
Write
LEFT JOIN income, or leave income out until the last query2,497 of the 10,011 people in the database have no income row. With an inner join, you lose them with no warning, and one of the three women is in that group. See Part 2, step 2.
-
You typed
WHERE event_name = 'SQL Symphony Concert' (no date)You get 25 people with 3 visits, not 2
Write
date BETWEEN 20171201 AND 20171231Without the date filter, you count visits from all of the data, not from December 2017. See Part 2, step 3.
SQL Murder Mystery walkthrough, part 1: who is the killer?
The killer in the SQL Murder Mystery is one man, and 7 queries get you from the crime scene report to his name. Each step shows a first hint, then the query and its real result. Open the second hint if the first one is not enough.
Part 1, step 1: Find the crime scene report
Get the report for the murder on January 15, 2018, in SQL City.
Hint 1: The report is in the crime_scene_report table. The prompt gives you three facts that pick one row.
Hint 2
Filter on date, type and city. The date column is a number in the form 20180115, not a date. Without the city filter, the query returns 3 murders on that date: in Albany, Reno and SQL City.
The query
SELECT *
FROM crime_scene_report
WHERE date = 20180115
AND type = 'murder'
AND city = 'SQL City'; The result
| date | type | description | city |
|---|---|---|---|
| 20180115 | murder | Security footage shows that there were 2 witnesses. The first witness lives at the last house on "Northwestern Dr". The second witness, named Annabel, lives somewhere on "Franklin Ave". | SQL City |
What you learn: Two witnesses: the last house on Northwestern Dr, and a person named Annabel on Franklin Ave.
Part 1, step 2: Find the first witness
Find the person who lives at the last house on Northwestern Dr.
Hint 1: Addresses are in the person table, in the columns address_number and address_street_name.
Hint 2
50 people live on Northwestern Dr. The last house has the highest address_number, so sort from high to low and keep the first row.
The query
SELECT id, name, address_number, address_street_name
FROM person
WHERE address_street_name = 'Northwestern Dr'
ORDER BY address_number DESC
LIMIT 1; The result
| id | name | address_number | address_street_name |
|---|---|---|---|
| 14887 | Morty Schapiro | 4919 | Northwestern Dr |
What you learn: The first witness is person 14887.
Part 1, step 3: Find the second witness
Find the person named Annabel who lives on Franklin Ave.
Hint 1: Use the person table again. You know only the first name, so match the start of the name.
Hint 2
LIKE 'Annabel%' matches every name that starts with Annabel. 4 people match it, so also filter on the street.
The query
SELECT id, name, address_number, address_street_name
FROM person
WHERE address_street_name = 'Franklin Ave'
AND name LIKE 'Annabel%'; The result
| id | name | address_number | address_street_name |
|---|---|---|---|
| 16371 | Annabel Miller | 103 | Franklin Ave |
What you learn: The second witness is person 16371.
Part 1, step 4: Read the two interviews
Read what the two witnesses told the police.
Hint 1: The interview table has a person_id column. Use the two ids that you found.
Hint 2
IN (14887, 16371) matches both ids in one query.
The query
SELECT person_id, transcript
FROM interview
WHERE person_id IN (14887, 16371); The result
| person_id | transcript |
|---|---|
| 14887 | I heard a gunshot and then saw a man run out. He had a "Get Fit Now Gym" bag. The membership number on the bag started with "48Z". Only gold members have those bags. The man got into a car with a plate that included "H42W". |
| 16371 | I saw the murder happen, and I recognized the killer from my gym when I was working out last week on January the 9th. |
What you learn: The killer is a man, a gold member of Get Fit Now Gym with a membership id that starts with 48Z. The second witness saw him at the gym on January 9. His car plate includes H42W.
Part 1, step 5: Check the gym clue
Find the gold members with a 48Z id who were at the gym on January 9, 2018.
Hint 1: Members are in get_fit_now_member. Visits are in get_fit_now_check_in. The two tables share the membership id.
Hint 2
Join the tables on get_fit_now_check_in.membership_id = get_fit_now_member.id. Then filter on the id, the gold status and check_in_date = 20180109.
The query
SELECT get_fit_now_member.id, get_fit_now_member.name,
get_fit_now_member.membership_status
FROM get_fit_now_member
JOIN get_fit_now_check_in
ON get_fit_now_check_in.membership_id = get_fit_now_member.id
WHERE get_fit_now_member.id LIKE '48Z%'
AND get_fit_now_member.membership_status = 'gold'
AND get_fit_now_check_in.check_in_date = 20180109; The result
| id | name | membership_status |
|---|---|---|
| 48Z7A | Joe Germuska | gold |
| 48Z55 | Jeremy Bowers | gold |
What you learn: Two members match, so the gym clue alone does not decide. Use the car plate.
Part 1, step 6: Check the car plate
Find the people whose car plate includes H42W.
Hint 1: Plates are in drivers_license. Names are in person. person.license_id points to drivers_license.id.
Hint 2
LIKE '%H42W%' matches H42W at any position in the plate.
The query
SELECT person.name, drivers_license.plate_number, drivers_license.gender
FROM person
JOIN drivers_license ON drivers_license.id = person.license_id
WHERE drivers_license.plate_number LIKE '%H42W%'; The result
| name | plate_number | gender |
|---|---|---|
| Tushar Chandra | 4H42WR | male |
| Jeremy Bowers | 0H42W2 | male |
| Maxine Whitely | H42W0X | female |
What you learn: One name is in both lists: Jeremy Bowers.
Part 1, step 7: Put the clues in one query
Get the one person who has an H42W plate and a gold membership with a 48Z id.
Hint 1: Join three tables: person, drivers_license and get_fit_now_member.
Hint 2
get_fit_now_member.person_id points to person.id. Keep the plate filter, the 48Z id filter and the gold filter.
The query
SELECT person.id, person.name
FROM person
JOIN drivers_license ON drivers_license.id = person.license_id
JOIN get_fit_now_member ON get_fit_now_member.person_id = person.id
WHERE drivers_license.plate_number LIKE '%H42W%'
AND get_fit_now_member.id LIKE '48Z%'
AND get_fit_now_member.membership_status = 'gold'; The result
| id | name |
|---|---|
| 67318 | Jeremy Bowers |
What you learn: The killer is Jeremy Bowers, person 67318.
A shorter path to the killer: The Funky Grooves Tour
After steps 2 and 3, you have the ids of both witnesses. On January 15, 2018, both of them checked in at the same event, The Funky Grooves Tour. Only one other person checked in there that day:
SELECT person.id, person.name
FROM facebook_event_checkin
JOIN person ON person.id = facebook_event_checkin.person_id
WHERE facebook_event_checkin.date = 20180115
AND facebook_event_checkin.event_id IN (
SELECT event_id
FROM facebook_event_checkin
WHERE person_id IN (14887, 16371)
AND date = 20180115
); | id | name |
|---|---|
| 14887 | Morty Schapiro |
| 16371 | Annabel Miller |
| 67318 | Jeremy Bowers |
With this path, you skip the interviews, the gym and the car plate. You still get the correct reply for his name, but to see why he is the killer, read steps 4 to 7.
SQL Murder Mystery walkthrough, part 2: who hired the killer?
Part 2 of the SQL Murder Mystery starts from the killer's interview, and it ends with the name of the person who paid him. Each step shows a first hint, then the query and its real result.
Part 2 in 2 queries
The game challenges you to finish part 2 in no more than 2 queries. You can:
- Read the killer's interview: Part 2, step 1: Read the killer's interview.
- Put every clue in one query, with a subquery for the concert: Part 2, step 4: Put the clues in one query.
Steps 2 and 3 show the same clues one at a time, if you want to see each list first.
Part 2, step 1: Read the killer's interview
Read what Jeremy Bowers told the police.
Hint 1: Use the interview table with his person id.
Hint 2
His person id is 67318.
The query
SELECT transcript
FROM interview
WHERE person_id = 67318; The result
| transcript |
|---|
| I was hired by a woman with a lot of money. I don't know her name but I know she's around 5'5" (65") or 5'7" (67"). She has red hair and she drives a Tesla Model S. I know that she attended the SQL Symphony Concert 3 times in December 2017. |
What you learn: A woman, 65 to 67 inches tall, red hair, a Tesla Model S, at the SQL Symphony Concert 3 times in December 2017.
Part 2, step 2: Find the women who match the description
Find the drivers who match her height, hair and car.
Hint 1: Height, hair color, gender and car are all columns of drivers_license.
Hint 2
BETWEEN 65 AND 67 keeps both ends. Without the height filter, 3 drivers still match, so you need the concert clue too.
The query
SELECT person.id, person.name, drivers_license.height
FROM person
JOIN drivers_license ON drivers_license.id = person.license_id
WHERE drivers_license.gender = 'female'
AND drivers_license.hair_color = 'red'
AND drivers_license.car_make = 'Tesla'
AND drivers_license.car_model = 'Model S'
AND drivers_license.height BETWEEN 65 AND 67; The result
| id | name | height |
|---|---|---|
| 78881 | Red Korb | 65 |
| 90700 | Regina George | 66 |
| 99716 | Miranda Priestly | 66 |
What you learn: Three women match. The concert clue decides.
Part 2, step 3: Find who went to the concert 3 times
Find the people who checked in at the SQL Symphony Concert 3 times in December 2017.
Hint 1: Check-ins are in facebook_event_checkin, one row for each check-in.
Hint 2
Count the rows for each person_id with GROUP BY, and keep the groups where the count is 3 with HAVING.
The query
SELECT person_id, COUNT(*) AS visits
FROM facebook_event_checkin
WHERE event_name = 'SQL Symphony Concert'
AND date BETWEEN 20171201 AND 20171231
GROUP BY person_id
HAVING COUNT(*) = 3; The result
| person_id | visits |
|---|---|
| 24556 | 3 |
| 99716 | 3 |
What you learn: Person 99716 is in both lists: Miranda Priestly.
Part 2, step 4: Put the clues in one query
Get the one woman with red hair and a Tesla Model S who went to the concert 3 times.
Hint 1: Put the concert query inside the description query as a subquery.
Hint 2
Use person.id IN (the concert query). Join income on ssn if you want to see her money too.
The query
SELECT person.name, income.annual_income
FROM person
JOIN drivers_license ON drivers_license.id = person.license_id
JOIN income ON income.ssn = person.ssn
WHERE drivers_license.gender = 'female'
AND drivers_license.hair_color = 'red'
AND drivers_license.car_make = 'Tesla'
AND drivers_license.car_model = 'Model S'
AND person.id IN (
SELECT person_id
FROM facebook_event_checkin
WHERE event_name = 'SQL Symphony Concert'
AND date BETWEEN 20171201 AND 20171231
GROUP BY person_id
HAVING COUNT(*) = 3
); The result
| name | annual_income |
|---|---|
| Miranda Priestly | 310000 |
What you learn: The person who hired the killer is Miranda Priestly.
Who are the suspects in the SQL Murder Mystery, and why is each one out?
Six people in the SQL Murder Mystery match a clue but not the full case. For each person, you see the clue that they match and the reason that they are out. Only Jeremy Bowers matches every clue in part 1, and only Miranda Priestly matches every clue in part 2.
| Suspect | Matches | Why they are out | Step |
|---|---|---|---|
| Joe Germuska | A gold 48Z member who was at the gym on January 9 | His license id, 173289, is not in drivers_license, so he has no car plate on file. | Part 1, step 5 |
| Tushar Chandra | A man with an H42W car plate | He is not a member of the Get Fit Now Gym. | Part 1, step 6 |
| Maxine Whitely | An H42W car plate | She is a woman, and the witness saw a man. She is not a gym member either. | Part 1, step 6 |
| Red Korb | Red hair, a Tesla Model S, 65 inches tall | She never checked in at the SQL Symphony Concert. | Part 2, step 2 |
| Regina George | Red hair, a Tesla Model S, 66 inches tall | She never checked in at the SQL Symphony Concert. She has no income row either. | Part 2, step 2 |
| Bryan Pardo | 3 visits to the SQL Symphony Concert in December 2017 | His license id, 101191, is not in drivers_license, so no hair color, height or car is on file for him. | Part 2, step 3 |
How does the SQL Murder Mystery check your answer?
Spoiler: this section shows how to read both names with no detective work.
The answer check in the SQL Murder Mystery is a SQLite trigger named check_solution on the
solution table. You can read the trigger with this query:
SELECT sql FROM sqlite_master WHERE type = 'trigger'; Here is the start of the trigger, with the two long replies cut:
CREATE TRIGGER check_solution AFTER INSERT ON solution
WHEN new.user==1
BEGIN
DELETE FROM solution;
INSERT INTO solution VALUES (0,
CASE WHEN hex(new.value)=='4A6572656D7920426F77657273' THEN "Congrats, you found the murderer! ..."
WHEN hex(new.value)=='4D6972616E6461205072696573746C79' THEN "Congrats, you found the brains behind the murder! ..."
ELSE "That's not the right person. Try again!" -
WHEN new.user==1: SQLite runs the check only when the first value is 1. WithINSERT INTO solution VALUES (2, 'Jeremy Bowers'), you get your own text back, not a reply. -
hex(new.value): SQLite changes your text to hexadecimal and compares it with a fixed value. For this reason, a different letter case or an extra space is a wrong answer. - The two fixed values are the two names in hexadecimal. Run this query to get the first one back as text:
SELECT CAST(X'4A6572656D7920426F77657273' AS TEXT) AS name; Players call this the zero-query solution. You get the names, but you get no practice with SQL, and the practice is the point of the game.
How do you play the SQL Murder Mystery on your computer?
Download the SQL Murder Mystery database, sql-murder-mystery.db (3.6 MB). It is the same file that the browser game loads, and the game's GitHub repository has it too. The file is a SQLite database, so you can open it in SQLite, or copy it into PostgreSQL or MySQL. We ran each walkthrough query in SQLite 3.50, PostgreSQL 17.5 and MySQL 8.4, and we got the same rows in all three.
SQLite
Open the file with the sqlite3 command-line tool, list the tables, and turn on the setting for the answer check:
sqlite3 sql-murder-mystery.db
.tables
.dbconfig dqs_dml on
Then type the queries at the sqlite> prompt. Without .dbconfig dqs_dml on, you get the
error "no such column" when you check an answer. A desktop program such as SQLiteStudio, which the game's
repository suggests, also opens the file.
Python and pandas
In Python, you need only pandas to read the file:
import sqlite3
import pandas as pd
con = sqlite3.connect('sql-murder-mystery.db')
print(pd.read_sql("SELECT * FROM crime_scene_report WHERE date = 20180115 AND type = 'murder' AND city = 'SQL City'", con))
# The answer check works in Python with no extra setting
con.execute("INSERT INTO solution VALUES (1, 'Name Here')")
print(con.execute("SELECT value FROM solution").fetchone()[0]) PostgreSQL and MySQL
Make an empty database named smm, then copy the tables with this Python script. To run it, install
pandas, SQLAlchemy and a driver: psycopg2-binary for PostgreSQL, or PyMySQL for MySQL. If your server needs a
password, put it in the address, for example postgres:your_password@localhost.
import sqlite3
import pandas as pd
from sqlalchemy import create_engine
source = sqlite3.connect('sql-murder-mystery.db')
target = create_engine('postgresql+psycopg2://postgres@localhost/smm')
# For MySQL: create_engine('mysql+pymysql://root@localhost/smm')
tables = pd.read_sql("SELECT name FROM sqlite_master WHERE type = 'table' AND name != 'solution'", source)['name']
for table in tables:
pd.read_sql(f'SELECT * FROM {table}', source).to_sql(table, target, index=False) You can run the walkthrough queries with no change. Five things are different:
| You run | SQLite (the game) | PostgreSQL | MySQL |
|---|---|---|---|
List the tables | SELECT name FROM sqlite_master WHERE type = 'table'; | \dt (in psql) | SHOW TABLES; |
type = 'Murder' AND city = 'sql city' | 0 rows | 0 rows | 1 row: MySQL ignores letter case |
name LIKE 'annabel%' | 4 rows | 0 rows: PostgreSQL LIKE matches letter case | 4 rows |
membership_status = 'Gold' | 0 rows | 0 rows | 68 rows: all of the gold members |
date = '2018-01-15' | 0 rows | Error: invalid input syntax for type bigint | 0 rows |
The answer check | Works | Not copied: it is a SQLite trigger | Not copied: it is a SQLite trigger |
Which SQL do you need for the SQL Murder Mystery?
You can solve the SQL Murder Mystery with WHERE, LIKE and one join at a time. The walkthrough also joins three tables and uses GROUP BY, HAVING and one subquery, so that each step is one query. Each row names the first walkthrough step that uses the SQL, with a free lesson for it.
| SQL | First used in | Free lesson |
|---|---|---|
WHERE | Part 1, step 1: Find the crime scene report | WHERE |
AND | Part 1, step 1: Find the crime scene report | AND, OR and NOT |
ORDER BY | Part 1, step 2: Find the first witness | ORDER BY |
LIMIT | Part 1, step 2: Find the first witness | LIMIT |
LIKE | Part 1, step 3: Find the second witness | BETWEEN, IN and LIKE |
IN | Part 1, step 4: Read the two interviews | BETWEEN, IN and LIKE |
INNER JOIN | Part 1, step 5: Check the gym clue | INNER JOIN |
A join on three tables | Part 1, step 7: Put the clues in one query | Joins on more than two tables |
BETWEEN | Part 2, step 2: Find the women who match the description | BETWEEN, IN and LIKE |
GROUP BY | Part 2, step 3: Find who went to the concert 3 times | GROUP BY |
HAVING | Part 2, step 3: Find who went to the concert 3 times | HAVING |
A subquery in WHERE | Part 2, step 4: Put the clues in one query | Subqueries in WHERE |
What should you play after the SQL Murder Mystery?
After the SQL Murder Mystery, play SQL Block Game. The murder case uses WHERE in every step, and ORDER BY, LIMIT and GROUP BY too. SQL Block Game drills those clauses at speed. Query cards fall, and you write the SQL that answers each one before it lands. It runs real PostgreSQL in your browser, it is free, and every run shuffles the cards.
Play SQL Block Game →For more detective games and how they compare, read our ranking of the best SQL games. For a story that teaches window functions and recursive CTEs, play Station Zero.
SQL Murder Mystery questions
Who made the SQL Murder Mystery?
Joon Park and Cathy He made it while they were fellows at the Knight Lab of Northwestern University. Joe Germuska made the web version. The game is free, and you play it in your browser.
How long does the SQL Murder Mystery take?
About 45 minutes, in our own play. If you get stuck, open the hints for the step you are on.
The SQL Murder Mystery is by Joon Park and Cathy He, Northwestern University Knight Lab. The case text and the database rows quoted on this page are from the game's GitHub repository and are licensed CC BY-SA 4.0. SQLMaxx did not make this game.