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.

By Owen Middleton Updated October 29, 2026 Every query run on the game's database

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 67

    The 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 computer

    You get An error that starts with no such column: "Congrats, you found the murderer!

    Do this .dbconfig dqs_dml on

    The 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 page

    You 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 copiedYou getWrite
drivers_license.plateError: no such columndrivers_license.plate_number
crime_type = 'murder'Error: no such columntype = 'murder'
membership_status = 'Gold'0 rowsmembership_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 nowGo 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:

  1. Open mystery.knightlab.com and read the prompt: a murder, on January 15, 2018, in SQL City.
  2. 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.
  3. Write a query in one of the boxes and run it. Each result gives you a clue for the next query.
  4. Follow the clues through the tables until one person is left.
  5. Check your answer with the game's solution table.

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.

SQL Murder Mystery schema diagram: the 9 tables and the columns that link them person.license_id links to drivers_license.id. person.ssn links to income.ssn. interview.person_id links to person.id. facebook_event_checkin.person_id links to person.id. get_fit_now_member.person_id links to person.id. get_fit_now_check_in.membership_id links to get_fit_now_member.id. crime_scene_report and solution link to no other table. drivers_license idheighthair_colorgenderplate_numbercar_makecar_model income ssnannual_income solution uservalue person idnamelicense_idssnaddress_numberaddress_street_name crime_scene_report datetypedescriptioncity interview person_idtranscript facebook_event_checkin person_idevent_idevent_namedate get_fit_now_member idperson_idnamemembership_status get_fit_now_check_in membership_idcheck_in_date
TableWhat it holdsColumns that link
crime_scene_reportPolice reports: date (a number such as 20180115), type of crime, a description and the city.
personEvery person: name, address and the ids that link to other tables.id, license_id, ssn
interviewWhat people told the police.person_id
drivers_licenseAge, height, eye and hair color, gender, car and plate number.id
get_fit_now_memberMembers of the Get Fit Now Gym and their membership status.id (such as 48Z55), person_id
get_fit_now_check_inEach gym visit, with the date and times.membership_id
facebook_event_checkinEach check-in at an event, with the event name and date.person_id
incomeAnnual income.ssn
solutionWhere 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 = 20180115

    The 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 1

    You get the wrong person: Kinsey Erickson, house 309

    Write ORDER BY address_number DESC LIMIT 1

    Without 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 = 20180109

    Gym 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.ssn

    You get 2 women, not 3

    Write LEFT JOIN income, or leave income out until the last query

    2,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 20171231

    Without 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

datetypedescriptioncity
20180115murderSecurity 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

idnameaddress_numberaddress_street_name
14887Morty Schapiro4919Northwestern 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

idnameaddress_numberaddress_street_name
16371Annabel Miller103Franklin 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_idtranscript
14887I 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".
16371I 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

idnamemembership_status
48Z7AJoe Germuskagold
48Z55Jeremy Bowersgold

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

nameplate_numbergender
Tushar Chandra4H42WRmale
Jeremy Bowers0H42W2male
Maxine WhitelyH42W0Xfemale

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

idname
67318Jeremy 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
  );
idname
14887Morty Schapiro
16371Annabel Miller
67318Jeremy 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:

  1. Read the killer's interview: Part 2, step 1: Read the killer's interview.
  2. 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

idnameheight
78881Red Korb65
90700Regina George66
99716Miranda Priestly66

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_idvisits
245563
997163

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

nameannual_income
Miranda Priestly310000

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.

SuspectMatchesWhy they are outStep
Joe GermuskaA gold 48Z member who was at the gym on January 9His license id, 173289, is not in drivers_license, so he has no car plate on file.Part 1, step 5
Tushar ChandraA man with an H42W car plateHe is not a member of the Get Fit Now Gym.Part 1, step 6
Maxine WhitelyAn H42W car plateShe is a woman, and the witness saw a man. She is not a gym member either.Part 1, step 6
Red KorbRed hair, a Tesla Model S, 65 inches tallShe never checked in at the SQL Symphony Concert.Part 2, step 2
Regina GeorgeRed hair, a Tesla Model S, 66 inches tallShe never checked in at the SQL Symphony Concert. She has no income row either.Part 2, step 2
Bryan Pardo3 visits to the SQL Symphony Concert in December 2017His 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. With INSERT 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 runSQLite (the game)PostgreSQLMySQL
List the tablesSELECT name FROM sqlite_master WHERE type = 'table';\dt (in psql)SHOW TABLES;
type = 'Murder' AND city = 'sql city'0 rows0 rows1 row: MySQL ignores letter case
name LIKE 'annabel%'4 rows0 rows: PostgreSQL LIKE matches letter case4 rows
membership_status = 'Gold'0 rows0 rows68 rows: all of the gold members
date = '2018-01-15'0 rowsError: invalid input syntax for type bigint0 rows
The answer checkWorksNot copied: it is a SQLite triggerNot 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.

SQLFirst used inFree lesson
WHEREPart 1, step 1: Find the crime scene reportWHERE
ANDPart 1, step 1: Find the crime scene reportAND, OR and NOT
ORDER BYPart 1, step 2: Find the first witnessORDER BY
LIMITPart 1, step 2: Find the first witnessLIMIT
LIKEPart 1, step 3: Find the second witnessBETWEEN, IN and LIKE
INPart 1, step 4: Read the two interviewsBETWEEN, IN and LIKE
INNER JOINPart 1, step 5: Check the gym clueINNER JOIN
A join on three tablesPart 1, step 7: Put the clues in one queryJoins on more than two tables
BETWEENPart 2, step 2: Find the women who match the descriptionBETWEEN, IN and LIKE
GROUP BYPart 2, step 3: Find who went to the concert 3 timesGROUP BY
HAVINGPart 2, step 3: Find who went to the concert 3 timesHAVING
A subquery in WHEREPart 2, step 4: Put the clues in one querySubqueries 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.