๐Ÿ›ข️ 19 SQL Anti-Patterns That Are Slowing Down Your Database

SQL performance issues often come from common anti-patterns. Knowing these mistakes helps you write faster, cleaner, and maintainable queries.

1. ๐Ÿšซ SELECT * (Lazy Column Fetching)

-- BAD
SELECT * FROM users;

-- GOOD
SELECT id, username FROM users;

✅ Always select only the columns you need.

2. ๐Ÿ“ฆ Storing Comma-Separated Values

-- BAD: hobbies column "reading,swimming,gaming"
SELECT * FROM users WHERE hobbies LIKE '%swimming%';

-- GOOD: normalized table
CREATE TABLE user_hobbies (
    user_id INT,
    hobby VARCHAR(50)
);

✅ Normalize data for better performance and maintainability.

3. ๐Ÿ” LIKE with Leading Wildcards

-- BAD
SELECT * FROM products WHERE name LIKE '%phone';

-- GOOD
SELECT * FROM products WHERE name LIKE 'phone%';

✅ Avoid leading wildcards; consider full-text search if needed.

4. ๐Ÿงฑ Ignoring Indexes

CREATE INDEX idx_user_id ON orders(user_id);

✅ Index columns used in WHERE, JOIN, and ORDER BY clauses.

5. ๐Ÿ”„ Subqueries Instead of Joins

-- BAD
SELECT * FROM employees
WHERE department_id IN (SELECT id FROM departments WHERE name='Sales');

-- GOOD
SELECT e.* FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.name = 'Sales';

✅ Prefer joins for efficiency and readability.

6. ๐Ÿงฎ COUNT(*) on Large Tables

-- BAD
SELECT COUNT(*) FROM large_table;

-- GOOD
SELECT COUNT(id) FROM large_table;

✅ Count indexed columns instead of * on big tables.

7. ๐Ÿง  Using NULL for Missing Data

-- BAD
SELECT * FROM users WHERE age IS NULL;

-- GOOD
SELECT * FROM users WHERE age = 0; -- or use 'is_unknown' flag

✅ Use default values or flags when possible.

8. ๐Ÿ” Repeated Queries

-- BAD
SELECT * FROM users WHERE status='active';
SELECT * FROM users WHERE status='inactive';

-- GOOD
SELECT * FROM users WHERE status IN ('active','inactive');

✅ Consolidate queries for cleaner, faster code.

9. ๐Ÿงฉ Not Using Constraints

-- GOOD
ALTER TABLE orders ADD CONSTRAINT fk_customer
FOREIGN KEY (customer_id) REFERENCES customers(id);

✅ Use primary, foreign keys and unique constraints for integrity.

10. ๐Ÿงช Inefficient Transactions

-- BAD
BEGIN TRANSACTION;
UPDATE users SET status='active';
UPDATE orders SET status='processed';
COMMIT;

-- GOOD
BEGIN TRANSACTION;
UPDATE users SET status='active';
COMMIT;

BEGIN TRANSACTION;
UPDATE orders SET status='processed';
COMMIT;

✅ Keep transactions short and commit promptly.

11. ๐Ÿ›‘ Overusing DISTINCT

-- BAD
SELECT DISTINCT username FROM users;

-- GOOD
SELECT username FROM users
GROUP BY username;

✅ Avoid DISTINCT when possible; fix duplicates at the data level.

12. ⚖️ JOIN Order & Indexes

-- GOOD
CREATE INDEX idx_customer_id ON orders(customer_id);

✅ Index join columns; analyze execution plans.

13. ๐Ÿ•ณ Functions on Indexed Columns

-- BAD
SELECT * FROM users WHERE UPPER(username) = 'ADMIN';

-- GOOD
SELECT * FROM users WHERE username = 'ADMIN';

✅ Avoid functions in WHERE clauses on indexed columns.

14. ๐Ÿ”ข Inefficient Pagination

-- BAD
SELECT * FROM orders ORDER BY created_at LIMIT 50 OFFSET 100000;

-- GOOD
SELECT * FROM orders
WHERE created_at > '2025-09-01'
ORDER BY created_at
LIMIT 50;

✅ Use keyset pagination for large datasets.

15. ๐Ÿ— Batch Inserts/Updates

-- BAD
INSERT INTO users (username) VALUES ('user1');
INSERT INTO users (username) VALUES ('user2');

-- GOOD
INSERT INTO users (username) VALUES ('user1'), ('user2'), ('user3');

✅ Batching improves throughput and reduces disk I/O.

16. ๐Ÿ—‘ Archive Old Data

-- GOOD PRACTICE
INSERT INTO orders_archive SELECT * FROM orders WHERE created_at < '2023-01-01';
DELETE FROM orders WHERE created_at < '2023-01-01';

✅ Archive or partition old data to keep tables fast.

17. ๐Ÿ”’ Holding Locks Too Long

-- GOOD
BEGIN TRANSACTION;
UPDATE users SET status='active';
COMMIT;

✅ Short transactions prevent deadlocks and improve concurrency.

18. ๐ŸŒ Ignoring Execution Plans

EXPLAIN SELECT * FROM orders WHERE customer_id = 123;

✅ Use EXPLAIN or EXPLAIN ANALYZE to optimize queries and check index usage.

19. ⚠️ The N+1 Query Problem in JPA & Hibernate

One common and often overlooked anti‑pattern when using Spring Data JPA and Hibernate is the N+1 query problem, a performance issue that can dramatically increase database calls and slow down your application.

The “N+1” problem occurs when your ORM issues one query to load a set of parent entities, and then one additional query per parent to load related child entities — resulting in N+1 SQL statements for a single logical operation.


// BAD: might lead to N+1
List users = userRepository.findAll();
for (User user : users) {
    System.out.println(user.getRoles().size());
}

With lazy‑loaded associations (the default in JPA), accessing a child collection like user.getRoles() triggers a separate query for each user. If you have 100 users, this results in ~101 queries instead of 1.

๐Ÿ“ฆ How to Prevent It

  • JOIN FETCH – Fetch associated entities in the initial query:
    
        @Query("SELECT u FROM User u LEFT JOIN FETCH u.roles")
        List findAllWithRoles();
        
  • @EntityGraph – Declaratively specify associations to fetch:
    
        @EntityGraph(attributePaths = "roles")
        List findAll();
        
  • @BatchSize – Tell Hibernate to batch fetch lazy collections:
    
        @Entity
        @BatchSize(size = 10)
        public class User { … }
        
  • @Fetch(FetchMode.SUBSELECT) – Fetch collections using a subselect strategy (Hibernate‑specific).

Monitoring SQL logs with spring.jpa.show‑sql and tools like p6spy helps you spot N+1 patterns early.

Fixing N+1 issues is crucial in real applications — especially those with large datasets or microservices where each extra database round‑trip increases latency and resource usage.

Conclusion

Avoid these 19 SQL anti-patterns to improve performance, reduce load, and simplify maintenance. Always focus on **indexes, normalization, and efficient query design**.

Labels: SQL, Database, Performance, Optimization, Best Practices, Queries

Comments

Popular posts from this blog

๐Ÿ› ️ The Code Hut - Index

๐Ÿ›ก️ Thread-Safe Programming in Java: Locks, Atomic Variables & LongAdder

๐Ÿ›ก️ Resilience Patterns in Distributed Systems