Your SQL Query Has Been Lying to You
You wrote SELECT first. SQL read it sixth.
That's not a bug, and you're not bad at this. SQL runs its clauses in a completely different order than you type them, and the moment that clicks, three of the most baffling beginner errors quietly confess.
So. Moustache on. We're doing this like Poirot.
Here's your case. Aboard the train, find every role held by more than one person, worst offenders first:
SELECT role, COUNT(*) AS passenger_count
FROM suspect_info
WHERE case_id = 1
GROUP BY role
HAVING COUNT(*) > 1
ORDER BY passenger_count DESC
LIMIT 3;
You read that top to bottom. SQL does not. It works the case the way you would. Round everyone up before you accuse anyone.
- FROMRound up everyone on the train
- WHEREDismiss the ones with alibis
- GROUP BYSort the survivors by role
- HAVINGDismiss whole groups
- SELECTAnnounce what you found
- ORDER BYArrange it, most dramatic first
- LIMITName only your top suspects
Notice where SELECT lands. The thing you wrote first is second to last. You can't announce findings before you've done the investigation, and neither can SQL.
FROM gathers the room. Every soul on the train, unfiltered, standing in the dining car. You can't filter people you haven't assembled, which is exactly why nothing else runs until this does.
WHERE starts the dismissals, one person at a time. You. Not on this case? Out. And this happens before anyone's been grouped or counted. Remember that. It's the whole reason for the first two errors below.
GROUP BY sorts the survivors into piles. All the conductors together, all the passengers together. From here on, SQL thinks in groups, not people.
HAVING is WHERE's older sibling. It dismisses entire groups. Any role with just one person? Gone. It runs after grouping, which is the only reason it's allowed to say COUNT(*) when WHERE isn't.
SELECT finally decides what goes in the report. This is also where passenger_count is born. It did not exist one moment ago. File that away too.
ORDER BY arranges the reveal, biggest group first. And yes, it can use passenger_count, because that alias exists by now. Timing is everything.
LIMIT takes the top 3. Last, obviously. "Top 3" of an unsorted pile means nothing.
Three errors that now explain themselves
"Unknown column 'passenger_count' in WHERE clause"
You used the alias in WHERE (step 2). It isn't born until SELECT (step 5). You accused someone who hadn't walked in yet. Use HAVING instead.
"Invalid use of group function" in WHERE
You put COUNT(*) in WHERE. But WHERE runs before GROUP BY, so there are no groups yet and nothing to count. HAVING, again. Got an aggregate in the condition? It lives in HAVING.
"Why does ORDER BY accept my alias but WHERE won't?"
Not a bug, just the clock. ORDER BY runs at step 6, after the alias exists. WHERE runs at step 2, long before. Same alias, different moment in the story.
See the pattern? Nearly every confusing SQL error is a clause showing up at the wrong point in the timeline.
The cheat sheet (screenshot this)
| # | Clause | Does what | Works on |
|---|---|---|---|
| 1 | FROM | Gathers the rows | Tables |
| 2 | WHERE | Filters individuals | Rows |
| 3 | GROUP BY | Bundles rows into groups | Rows to groups |
| 4 | HAVING | Filters whole groups | Groups |
| 5 | SELECT | Picks columns, makes aliases | Groups/rows |
| 6 | ORDER BY | Sorts the result | Result set |
| 7 | LIMIT | Trims the result | Result set |
One caveat for the pedants. This is the logical order. The database is allowed to physically shuffle things for speed, as long as the answer comes out identical. For writing SQL that actually runs, this is the order that matters.
Now go accuse someone
Reading about it gets you halfway. Putting COUNT(*) in a WHERE clause, watching it explode, and knowing instantly why? That's the half that sticks.
The suspect_info table is real. It's Case 1 of Query the Murder, straight out of Murder on the Orient Express, and it's live in your browser right now. No account, no setup. Just you and a train full of liars.
Keep going: Learn SQL interactively | SQL learning roadmap | More articles