🔥 New: Full-Stack Next.js 15 Bootcamp — live cohort starts Jan 27. Grab your seat →
Tutorial • October 24, 2023

7 SQL Mistakes Every Developer Makes (And How to Fix Them)

SQL is everywhere and most devs write it badly.

You don't need to be a database administrator to write good SQL. But if you're a backend developer, you are writing SQL every day. And if you're like most of us, you're probably getting away with bad habits that will eventually bite you in production.

We've seen production databases slow to a crawl because of a single missing index. We've seen data corruption because a transaction wasn't wrapped. We've seen teams lose weeks of work because they relied on an ORM to design their schema.

Here are the seven most common SQL mistakes developers make, and the concrete steps to fix them.

Database query visualization showing complex joins

SELECT * in Production Queries

It's easy. It's fast to type. But it's a performance and security nightmare. Selecting every column from a table returns more data than your application needs, increasing network latency and memory usage. Worse, it exposes columns you might not want to return to the client (like password hashes or internal flags).

❌ Bad:
SELECT * FROM users WHERE id = 1;

✅ Good:
SELECT id, email, first_name FROM users WHERE id = 1;

Missing Indexes on Foreign Keys

When you join two tables, the database has to look up rows in the second table for every row in the first. If the column you're joining on isn't indexed, this becomes a full table scan. If you have a high-traffic table with a foreign key relationship, you need an index immediately.

❌ Slow:
SELECT * FROM orders o JOIN users u ON o.user_id = u.id;

✅ Fast:
CREATE INDEX idx_orders_user_id ON orders(user_id);

N+1 Queries from the ORM

This is the classic Laravel/ActiveRecord mistake. You fetch a list of 50 posts, then loop through them to get the author's name for each one. That's 1 query to get posts, plus 50 queries to get authors. If your API scales to 10,000 requests, you just killed your database.

❌ N+1 Problem:
posts.forEach(post => post.author);
(1 query for posts, 50 queries for authors)

✅ Eager Loading:
Post::with('author')->get();
(2 queries total)

Not Using Transactions

If you're updating multiple tables or performing a complex operation that depends on data consistency, you must wrap it in a transaction. Without one, if the second query fails, the first one remains committed, leaving your data in an inconsistent state.

BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

Storing Timestamps Without Timezone

Storing naive timestamps (without timezone info) is a recipe for bugs. If your server is in New York and your database is in London, naive timestamps will shift by 5 hours when you query them. Always use TIMESTAMPTZ (PostgreSQL) or WITH TIME ZONE (MySQL) to store the actual moment in time.

Not Using EXPLAIN

Developers often trust their intuition about how a query will run. But the database optimizer is smarter than you. Run EXPLAIN ANALYZE on your slow queries. It will tell you exactly how many rows were scanned, if indexes were used, and where the bottleneck is.

Letting the ORM Design Your Schema

ORMs are great for rapid development, but they often default to a normalized schema that is terrible for performance. They might create a separate table for every single relationship. If you care about speed, you need to understand the trade-offs between normalization and denormalization, and sometimes you need to write raw SQL migrations to fix it.

Master the Database

Stop writing bad SQL. Learn how to optimize queries like a senior engineer.

PostgreSQL

PostgreSQL vs MySQL: The 2023 Comparison

When to choose PostgreSQL for your next project and when MySQL is still the better choice for legacy systems.

Performance

Database Indexing 101

A deep dive into B-Trees, Hash indexes, and covering indexes. How to make your queries fly.

Tutorial

Building a REST API with Node & SQL

Step-by-step guide to building a scalable backend API from scratch using modern tooling.