The SQL style guide: conventions that keep team queries readable
Max Musing
Max MusingFounder and CEO of Basedash
· August 4, 2026

Max Musing
Max MusingFounder and CEO of Basedash
· August 4, 2026

A SQL style guide is a short set of rules a team agrees on for how to name, format, and structure queries. It covers things like whether keywords are uppercase or lowercase, how to indent, how to name columns and CTEs, and when to break a query into steps. Which style you pick matters less than whether everyone on the team writes SQL the same way, so that any query is fast to read, review, and reuse.
A style guide is for SQL that other people have to read: shared dashboard queries, dbt models, ad hoc analysis that gets pasted into a doc, or a query someone else will edit six months from now. If your SQL only ever runs once and only you see it, style matters less. Once a query is shared, consistency starts saving time.
If you only take away a few rules, take these:
snake_case for every table, column, and alias.with blocks), not nested subqueries.as when you alias a column or table.Everything below is the longer version, plus where well-known published guides disagree and how to get a team to follow the rules.
The value of a style guide shows up in three places.
Review speed. When every query is formatted the same way, a reviewer reads structure instead of decoding formatting. A diff that only changes logic is easy to approve. A diff where someone also re-indented the whole file hides the real change.
Onboarding. A new analyst who inherits a consistent codebase can predict where things are: imports at the top, one CTE per logical step, the final select at the bottom. Inconsistent SQL forces them to re-learn each author’s personal habits.
Reuse. Queries get copied. A well-named, well-structured query is safe to lift into a new dashboard. A dense, single-block query with aliases like t1 and x gets rewritten from scratch, which is how two slightly different versions of the same metric end up in production.
None of this requires the “best” style, only one that everyone shares.
Naming deserves the most attention, because names are what people read first.
| Element | Convention | Example |
|---|---|---|
| Tables | snake_case, collective or plural noun |
orders, customers |
| Columns | snake_case, singular, no table prefix |
order_total, email |
| Booleans | is_ or has_ prefix |
is_active, has_paid |
| Dates and timestamps | _date or _at suffix |
signup_date, created_at |
| CTEs | verbose, describe the transformation | paid_orders, revenue_by_month |
| Computed columns | name it as if it were a real column | sum(amount) as total_revenue |
A few rules that prevent most naming arguments:
camelCase. It is harder to scan than snake_case, which is the identifier convention both major public guides recommend (sqlstyle.guide, dbt Labs).tbl_ or sp_. The database already knows what is a table.events_joined_to_users describes a step. user_events sounds like a table.Formatting rules are the ones people fight about and the ones a machine can enforce for you. The specific choices matter less than making them once.
A workable default:
select, from, left join). Pick the opposite if you prefer, but decide.join, and each condition in a multi-part where on its own line.Compare the same query written two ways. Messy:
select id,name,sum(amt) total from Orders o join Customers c on o.cid=c.id where c.Country='US' group by 1,2
Clean:
with us_customers as (
select
customer_id,
name
from customers
where country = 'US'
),
customer_orders as (
select
customer_id,
sum(amount) as total_revenue
from orders
group by customer_id
)
select
us_customers.customer_id,
us_customers.name,
customer_orders.total_revenue
from us_customers
left join customer_orders
on us_customers.customer_id = customer_orders.customer_id
Both return the same rows, but only the second is safe to hand to a teammate.
The largest readability gain in SQL comes from building a query out of named steps. A common-table-expression (with block) lets you name each stage of the work and read the query top to bottom.
Two structural conventions from the dbt style guide are worth adopting directly:
Compare this to the alternative, where a select sits inside a from sits inside another select. Nested subqueries force you to read inside-out, and the intermediate results have no names. CTEs read in the order the work happens, and you can comment out the final select and swap in select * from any_cte to inspect any step while you build.
Joins are where ambiguity creeps in, so a few explicit rules help:
inner join, not bare join.orders.customer_id is unambiguous. A bare customer_id in a two-table join makes the reader guess.as for aliases. It is explicit and easy to scan.left join and reorder your from and join so the flow reads in one direction. A right join is often a sign you should swap which table you select from.Aliases are one place where good guides disagree, covered below.
Good SQL is mostly self-documenting if it is named and structured well, so comments should carry the information the code cannot.
-- exclude internal test accounts, see ticket 4821), a business rule, a known data quirk, or an intentional edge case.-- select the customer id adds nothing next to select customer_id.That last habit pays off most on queries that power a dashboard, where the next person needs to know the grain before they trust the number.
Even the most cited public guides make opposite choices, which is the strongest argument for the “pick one and be consistent” principle.
| Decision | sqlstyle.guide (Simon Holywell) | dbt Labs |
|---|---|---|
| Keyword case | UPPERCASE (SELECT) |
lowercase (select) |
| Indentation | right-aligned “river” down the middle | 4 spaces, left-aligned |
group by |
list the columns explicitly | group by 1, 2 (by position) |
| Table aliases | short correlation from first letters | avoid initialisms, prefix the full table name |
| Identifiers | snake_case |
snake_case |
Explicit as |
required | required |
They agree on the things that most affect readability (snake_case identifiers, explicit as) and diverge on the things that are mostly taste (case, alignment, group by style). When your team writes its own guide, spend your debate budget on the rows where they agree and pick a side on the rows where they do not.
One note on the group by row: grouping by position (group by 1, 2) is terse and common in analytics code, but it breaks silently if someone reorders the select list. Grouping by explicit column name is more verbose and safer. Either is fine, as long as you don’t mix them in the same codebase.
Drop this into your team wiki or the top of your dbt repo and edit the choices you disagree with. The specific rows matter less than having a written default.
Naming
snake_case for all tables, columns, and aliasesis_/has_ for booleans, _at/_date for time columnstbl_/sp_ prefixes, no camelCaseFormatting
Structure
inner join, left join)as for every aliasComments
A style guide that lives only in a doc gets ignored. Enforcement should be as automatic as possible so reviewers don’t spend time arguing about commas.
Review AI-generated SQL against the same guide. Assistants produce working SQL that ignores your conventions, so treat their output like any other contribution. Our checklist for reviewing AI-generated SQL covers what to check beyond formatting.
t1, t2, and x save keystrokes and cost every future reader time. Alias to something meaningful.Loosen the rules when following them costs more than it saves:
select * to eyeball a table does not need CTEs and a header comment.If another person will read or edit the query, style it. Otherwise, do what is fastest.
Should SQL keywords be uppercase or lowercase?
Either works, and the two most-cited public guides split on it: sqlstyle.guide uses uppercase, dbt Labs uses lowercase. What matters is that your team picks one and applies it everywhere. Lowercase has become common in modern analytics codebases because it is faster to type and reads well alongside lowercase identifiers, but uppercase keywords make the SQL skeleton stand out. Choose one, put it in your guide, and let a formatter enforce it.
What is the difference between a SQL style guide and a linter?
The style guide is the set of rules your team agrees on. A linter like SQLFluff is the tool that checks and often auto-fixes SQL against those rules. The guide records the decision, and the linter enforces it. You want both: the guide so people understand the intent, and the linter so the rules hold up without manual policing in every review.
Should I use CTEs or subqueries?
Prefer CTEs (with blocks) for anything a person will read. CTEs let you name each step and read the query top to bottom, while nested subqueries force inside-out reading and leave intermediate results unnamed. Modern databases plan CTEs and equivalent subqueries similarly in most cases, so the choice is mostly about readability. Reach for a subquery only for a small, obvious, single-use expression.
How do I get a team to follow a SQL style guide?
Automate it. Keep the guide in the repo next to the code, run a formatter and linter like SQLFluff in CI so violations fail the pull request before a human reviews it, and standardize where queries are written and shared so well-styled queries become the copy-paste template. Manual enforcement through code review alone rarely lasts, because reviewers get tired of flagging commas.
Does SQL style matter if a BI tool generates the query?
For the machine-generated SQL itself, no: query builders and BI tools emit SQL that people don’t edit by hand, so readability rules do not apply to it. Style matters for the SQL your team writes by hand, including the custom queries you write inside a BI tool to power a chart or dashboard. Those are shared, reused, and reviewed like any other code, so the same conventions apply.
Written by

Founder and CEO of Basedash
Max Musing is the founder and CEO of Basedash, an AI-native business intelligence platform designed to help teams explore analytics and build dashboards without writing SQL. His work focuses on applying large language models to structured data systems, improving query reliability, and building governed analytics workflows for production environments.
Basedash lets you build charts, dashboards, and reports in seconds using all your data.