Ian Klosowicz

You write SQL other people can read by writing for the next person instead of the database. Name things so the query explains itself, structure it with CTEs that read top to bottom, format it vertically so the shape is visible, comment the reasoning behind the tricky parts, and keep each step doing one job. The database runs any query that parses. A teammate, a reviewer, or the version of you that opens this query in 6 months needs more than that, and a few habits give it to them.
Readable SQL is a set of small, repeatable choices you make every time you write a query, and none of them depend on fancy syntax. Here is the method, section by section, with the specific moves that make a query something another person can pick up without a walkthrough.
Readable SQL matters because queries get read far more often than they get written. A query you write once gets reopened every time it breaks, every time someone builds on it, and every time an analyst inherits your dashboard.
Think about who reads your SQL:
My first data job was in marketing analytics on BigQuery, and most of what I did early was inherit queries other people wrote. The ones I could read, I could fix in 10 minutes. The ones I could not, I rewrote from scratch, because trusting them was slower than starting over. That is the cost of unreadable SQL: nobody trusts it, so it gets rewritten instead of reused.
The one habit that helps readability most is naming. Good names turn a query into something you can read like a sentence, and bad names force the reader to hold the whole thing in their head.
Three places names matter most:
The test for a name is simple: could someone guess what it holds without reading the logic that built it? If yes, the name is carrying its weight. If they have to trace the query to understand the name, rename it.
Structure your query as a sequence of CTEs so a reader moves through it top to bottom, the same way they read everything else. The alternative, subqueries nested inside subqueries, forces people to read inside out, which nobody does naturally.
A CTE, written with the WITH clause, lets you break a query into named steps. Each step builds on the one above it:
Read that way, the query tells a story: here is the raw set, here is the version grouped by month, here is that joined to targets, here is the output. I lean on this constantly in Snowflake, where other people build models on top of mine. A query someone else will extend has to be legible first, and CTEs are the main tool that gets it there. If you want to drill this on realistic tables, it is one of the core SQL habits built into the daily tasks inside Analyst Hive.
Format your query so its shape shows the logic, because a reader scans structure before they read words. A query crammed onto a few long lines hides where the joins and filters are. A vertically formatted one puts them where the eye expects them.
The habits that cover most of it:
Consistency matters as much as the specific style. A team that formats every query the same way can diff two versions and see exactly what changed, which turns code review from guesswork into reading. Most editors will auto-format on save, so once you pick a style the cost drops to zero.
Comment the reasoning a reader can't get from the code itself. The SQL already shows what it does. What it can't show is why you made a choice that looks odd, and that is exactly where the next person gets stuck.
Good comments explain decisions:
Skip comments that just restate the code, like a note saying "join orders to customers" above a line that plainly joins orders to customers. That kind of comment adds reading without adding understanding, and it goes stale the moment the code changes. Comment the thinking, not the syntax, and the reader gets the context that is otherwise locked in your head.
Keep each CTE and each query focused on a single job, because a step that does one thing is a step a reader can understand on its own. The temptation is to be clever and do filtering, joining, aggregating, and ranking all in one dense block. It works, and it is unreadable.
When one CTE tries to do everything, the reader has to untangle all of it at once to understand any of it. Split it, and each piece becomes checkable on its own: this CTE filters, this one aggregates, this one ranks. You can select from any single step to confirm it does what its name says, which is also how you debug faster.
This runs against the instinct to make SQL short. Clarity matters more than line count. A query that is 10 lines longer but splits cleanly into named steps is easier to work with than a compact one nobody can follow, every time someone other than you opens it.
Before you call a query done, run it past a quick test: could a teammate who has never seen it understand what it does in about 30 seconds, without you in the room to explain it? If not, it is not finished.
A few concrete checks that stand in for that teammate:
This takes under a minute and catches most of what makes a query hard to inherit. Building the habit of running it is what turns readable SQL from a thing you think about into a thing you do automatically. That kind of deliberate daily practice on real queries is the whole model behind Analyst Hive.
Why does readable SQL matter if the query already works?
Because working and maintainable are different. A query that runs but nobody can read gets rewritten the next time it needs a change, which wastes the effort you put in. Readable SQL survives, gets built on, and passes code review faster. The person it helps most is usually future you, who will not remember the logic.
Are CTEs or subqueries better for readability?
CTEs, in most cases. They let you name each step and read a query top to bottom instead of untangling nested subqueries from the inside out. You can also select from any CTE to see what it returns, which makes debugging faster. Performance is similar in modern databases, so readability is the deciding factor.
How many comments should SQL have?
Enough to explain the choices a reader can't infer, and no more. Comment why a filter exists, why a value is hardcoded, or why you took an unusual approach. Skip comments that restate what the code plainly does, because they add noise and go stale. Aim for a few meaningful notes, not a comment on every line.
What is the best way to format a SQL query?
Put each major clause and each selected column on its own line, indent conditions under their clause, and pick one keyword case and hold it. The specific style matters less than applying it consistently, because consistency is what lets a team diff two versions and see what changed. Most editors auto-format on save.
Should I use short or descriptive aliases?
Descriptive enough that the reader knows the source without looking it up. A single letter is fine when it maps clearly to the table, like c for customers. Random labels like t1 and t2 force the reader to trace the query. The goal is that any prefixed column tells you where it came from at a glance.
Does readable SQL run slower?
No. Formatting, naming, comments, and CTE structure are about how the text reads, not how the database executes it. The engine ignores whitespace and comments entirely, and CTEs perform similarly to equivalent subqueries in modern databases. You get readability at no runtime cost, which is why there is little reason to skip it.
Readable SQL comes down to writing for the next person: clear names, a top-to-bottom structure, consistent formatting, comments that explain the why, and steps that each do one job. None of it is advanced, and all of it is a habit you build by doing it on every query until it stops being a decision. If you want that practice on realistic tables, with feedback on what actually reads clearly to someone else, that is what Analyst Hive is built for: a 90-day daily-task program for aspiring data analysts who want SQL other people can pick up and trust.