Engineering2026-07-21

The SQL Query That Took 3 Hours to Debug (Don\x27t Make My Mistake)

I spent an entire afternoon chasing a ghost in my database. The culprit? Something a basic formatting tool would\x27ve caught in five minutes flat.

Let me set the scene. It's 2 PM on a Thursday. I've got my coffee, my noise-canceling headphones, and a SQL query that needs to join four tables and spit out a report by 5 PM. No big deal, right?

Famous last words.

The Query From Hell

The query was about 120 lines long. Not huge, but chunky. It had nested subqueries, a couple of CTEs, more JOINs than a wedding reception, and — I now realize — zero formatting. It was a wall of text. UPPERCASE keywords slammed against lowercase column names with no spacing, no indentation, no mercy.

Here's basically what I was looking at (I've anonymized it, but the horror is the same):

SELECT a.id, b.name, c.total, (SELECT COUNT(*) FROM orders o WHERE o.user_id = a.id AND o.status = 'active') as order_count FROM users a LEFT JOIN profiles b ON a.id = b.user_id INNER JOIN (SELECT user_id, SUM(amount) as total FROM transactions GROUP BY user_id) c ON a.id = c.user_id WHERE a.created_at > '2025-01-01' AND b.name IS NOT NULL AND (SELECT COUNT(*) FROM orders o WHERE o.user_id = a.id AND o.status = 'pending') > 5;

Does that hurt your eyes? Good. That's exactly how I felt when I wrote it. But it worked. Mostly.

The Bug

The report came back with 47 records. I was expecting about 1,200. Something was filtering out almost all my users, but I couldn't figure out what. I stared at that goop for an hour. I added comments. I broke it into parts. I SELECT *'d every CTE individually. Nothing.

At hour two, I was convinced the database was haunted. I started muttering about "data ghosts." My coworker Sarah gave me a look that said please go home.

The Fix

Then, out of sheer desperation, I pasted the entire thing into our SQL formatter. Not because I thought it would help — just because I wanted the indentation to be pretty while I failed.

And there it was. Immediately. In plain sight.

The AND b.name IS NOT NULL clause. It was inside the subquery scope for the INNER JOIN, but I hadn't indented it, so I didn't see that it was excluding every user who didn't have a profile AND a pending order. Except oh wait — actually, because of operator precedence with a missing set of parentheses, it was being applied to the WHERE in a way that nuked three-quarters of my results.

Five minutes after pasting it into the formatter, I had the bug fixed. Three hours of my life, gone. For want of some whitespace.

Don't Be Me

I know formatting feels like a "nice to have." It's not. It's like wearing a seatbelt — boring until you get into a wreck. Format your SQL before you run it. Not after. If I had formatted that query upfront, I'd have spotted the janky parentheses in about eight seconds instead of three hours.

Use the SQL formatter. Use it early. Use it often. Your evening self will thank you — and you won't end up blaming ghosts for your own missing parenthesis.