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 or ROW_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}}