How to Write SQL Other People Can Read

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.

Table of contents

Why readable SQL is worth the effort

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:

  • The reviewer approving your pull request, who has to understand it fast enough to trust it.
  • The teammate who builds a new report on top of your query 3 months later.
  • Future you, who will not remember why you wrote that CASE statement the way you did.

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.

Name things so the query explains itself

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:

  • Table aliases. Alias customers as c and orders as o, not t1 and t2. When the reader sees o.total they know instantly which table it came from. Single letters are fine when they map to the table; random labels are not.
  • CTE names. Name each CTE for what it produces, like active_users or monthly_revenue, so the final query reads as a list of plain-English steps. A CTE called temp1 tells the reader nothing.
  • Column aliases. Rename computed columns to what they mean. SUM(amount) AS total_revenue beats a column the reader has to decode. If a column needs a comment to explain its name, the name is doing the wrong job.

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 with CTEs that read top to bottom

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:

  1. Start with the base data each CTE needs, pulled and lightly filtered.
  2. Add a CTE that shapes or aggregates it, named for the result.
  3. Add another that joins or filters further, again named for what it produces.
  4. End with a short final SELECT that pulls the pieces together.

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 vertically and consistently

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:

  • Put each major clause on its own line: SELECT, FROM, each JOIN, WHERE, GROUP BY, ORDER BY.
  • List each selected column on its own line, so adding or removing one is a clean one-line change in a code review.
  • Indent join conditions and WHERE conditions under their clause, so the reader sees what belongs to what.
  • Pick one case convention for keywords, uppercase or lowercase, and hold it across the whole query.

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 why, not the what

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:

  • Why a filter exists: a note that you exclude internal test accounts because they skew the totals.
  • Why a value is hardcoded: a note that a cutoff date marks when a tracking change took effect.
  • Why you chose an unusual approach: a note that you used a specific join because the obvious one double counts.

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 step doing one job

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.

A 30 second readability test before you ship

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:

  • Read only the CTE names and the final SELECT. Do they describe the query on their own? If the names tell the story, the structure is right.
  • Scan for any alias or column name you would have to trace to understand. Rename it.
  • Find the one part you would have to explain out loud. Put that explanation in a comment.
  • Check that the formatting is consistent enough to diff cleanly against a future version.

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.

FAQ

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.

Where to go from here

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.