SQL Anti-Patterns That Cost You Hours (and Your Server Performance)

If your SQL queries are “working” but taking forever to run — it’s probably not your database. It’s you. 💥
As analysts and developers, we tend to learn just enough SQL to get the job done. But over time, these small bad habits compound into real performance problems — slow reports, timeouts, or worse: results you can’t trust.
Here are some of the most common SQL anti-patterns I’ve seen (and made!) — and what to do instead.
🚫 1. SELECT * (and Pray)
What happens: You pull every column “just in case” — even when you only need 3 out of 30.

Why it’s bad:
- Slower queries
- More memory usage
- Breaks if column order or schema changes
- Sends more data than needed across the network
What to do instead:
- Be explicit:
SELECT customer_id, purchase_date, total_amount
- Only pull the fields you need for that query or visual
🟢 Rule of thumb: If it ends up in production, SELECT *
is a red flag.
🐌 2. Too Many Subqueries (When JOINs Will Do)
What happens:
You nest multiple SELECT
statements inside each other — sometimes 3, 4, or 5 levels deep — because it “feels safer.”

Why it’s bad:
- Harder to read and debug
- Often slower to execute
- Can cause unnecessary duplication
What to do instead:
- Use
JOIN
+WITH
(CTEs) to cleanly separate logic - Think in layers, not tunnels
🟢 Use subqueries for isolation, not structure.
🌀 3. Repeating Aggregations in Every Line
What happens:
You write something like SUM(...)
multiple times across your query, or call expensive CASE logic inside SELECT
, WHERE
, and GROUP BY
.

Why it’s bad:
- Redundant computations
- Slower performance on large datasets
- Harder to maintain or update
What to do instead:
- Use CTEs or variables to define once, reuse often
- Cache intermediate results where possible
🟢 Write like your query is a report, not a calculator.
🧯 4. Using DISTINCT to Fix Dirty Joins
What happens:
You join two tables and end up with duplicate rows — so you slap DISTINCT
at the top instead of fixing the root cause.

Why it’s bad:
- Hides data quality issues
- Can remove valid duplicates
- Turns your query into a guessing game
What to do instead:
- Understand your join keys
- Use
GROUP BY
orROW_NUMBER()
if intentional - Validate cardinality before joining
🟢 DISTINCT is a patch, not a solution.
✅ Wrap-Up: Write Queries for Humans (and Servers)
Clean SQL is fast SQL. But more importantly — it’s readable, explainable, and trustworthy.
These anti-patterns creep in when we’re rushing to get something working. But slowing down to structure your query right will save you hours later — in debugging, rerunning, and explaining.
📚 Further Reading
- SQL Style Guide — Mode Analytics
- SQL Anti-Patterns Book — Bill Karwin
- CTEs vs Subqueries — SQLShack
{{quiz}}