DEV Community

楊東霖
楊東霖

Posted on • Originally published at devtoolkit.cc

SQL Formatting Best Practices: Write Clean, Readable Queries

SQL is one of the most universally used languages in software development. Whether you are building data pipelines, writing reports, or debugging production issues, you will read and write SQL queries every day. Yet badly formatted SQL remains one of the most common sources of confusion, bugs, and wasted time on engineering teams.

This guide covers everything you need to know about SQL formatting best practices — from keyword casing and indentation to CTE alignment, naming conventions, and tooling. By the end, you will have a clear set of rules you can adopt immediately and share with your team.

Why SQL Formatting Matters

Before diving into specific rules, it is worth understanding why formatting matters at all. Some developers dismiss formatting as cosmetic. It is not. Consistent SQL formatting delivers concrete benefits:

  • Faster code reviews — reviewers can scan well-formatted queries in seconds instead of minutes. When every query follows the same structure, deviations and logic errors jump out immediately.
  • Fewer bugs — misplaced AND/OR conditions, accidental cross joins, and missing GROUP BY columns are far easier to spot in formatted SQL.
  • Easier onboarding — new team members can read the codebase without asking "what does this query do?" every five minutes.
  • Better diffs — when each clause sits on its own line, version control diffs show exactly what changed. A one-line query that wraps at 400 characters produces unreadable diffs.
  • Reduced cognitive load — you can hold the query structure in your head when it follows a predictable pattern.

Teams that adopt a shared SQL style guide report measurably faster development cycles. The investment in formatting pays for itself within the first week.

Common SQL Formatting Rules

1. Keyword Casing

The most debated formatting decision is whether SQL keywords should be uppercase or lowercase. Both are syntactically valid. The industry has largely settled on uppercase keywords as the standard:

-- Recommended: uppercase keywords
SELECT
    first_name,
    last_name,
    email
FROM users
WHERE is_active = 1
ORDER BY last_name ASC;

-- Avoid: lowercase keywords (harder to distinguish from column names)
select
    first_name,
    last_name,
    email
from users
where is_active = 1
order by last_name asc;
Enter fullscreen mode Exit fullscreen mode

Uppercase keywords create a clear visual distinction between SQL syntax and your data model (table names, column names). This makes queries scannable at a glance. If your team prefers lowercase, that is fine — the important thing is consistency. Pick one style and enforce it everywhere.

2. Indentation

Use consistent indentation to show the hierarchical structure of your query. The most common convention is 4 spaces (not tabs) for column lists and conditions:

SELECT
    u.first_name,
    u.last_name,
    o.order_date,
    o.total_amount
FROM users u
INNER JOIN orders o
    ON u.id = o.user_id
WHERE o.order_date >= '2026-01-01'
    AND o.total_amount > 100
ORDER BY o.order_date DESC;
Enter fullscreen mode Exit fullscreen mode

Notice how the column list is indented under SELECT, the join condition is indented under the JOIN, and additional WHERE conditions are indented under the first condition. This indentation makes the query's logical structure immediately visible.

3. One Column Per Line

Always place each column on its own line in SELECT, GROUP BY, and ORDER BY clauses. This rule is non-negotiable for queries with more than two or three columns:

-- Good: one column per line
SELECT
    customer_id,
    first_name,
    last_name,
    email,
    created_at
FROM customers;

-- Bad: all columns on one line
SELECT customer_id, first_name, last_name, email, created_at FROM customers;
Enter fullscreen mode Exit fullscreen mode

One column per line makes it trivial to add, remove, or reorder columns. It also produces clean diffs in version control — adding a column shows as a single added line instead of a rewrite of the entire SELECT clause.

4. Leading Commas vs. Trailing Commas

This is another hotly debated topic. Leading commas place the comma at the beginning of each line; trailing commas place it at the end:

-- Trailing commas (more common)
SELECT
    first_name,
    last_name,
    email
FROM users;

-- Leading commas (easier to comment out lines)
SELECT
    first_name
    , last_name
    , email
FROM users;
Enter fullscreen mode Exit fullscreen mode

Both styles have merit. Trailing commas are more natural to read and are the dominant convention. Leading commas make it easier to comment out the last column without worrying about a dangling comma. Choose one and stick with it across your entire codebase.

5. Clause Alignment

Major SQL clauses (SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT) should each start on their own line at the same indentation level:

SELECT
    department,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary
FROM employees
WHERE hire_date >= '2024-01-01'
GROUP BY department
HAVING COUNT(*) >= 5
ORDER BY avg_salary DESC
LIMIT 10;
Enter fullscreen mode Exit fullscreen mode

This left-aligned clause structure creates a clear visual "spine" on the left side of the query. You can instantly see the query's overall shape: what it selects, where the data comes from, how it filters, and how it orders.

Naming Conventions

Formatting is not just about whitespace and keywords. Consistent naming conventions are equally important for readability.

Table Names

  • Use snake_case for table names: user_accounts, not UserAccounts or useraccounts
  • Use plural nouns for tables: orders, customers, products
  • Prefix junction tables with both entity names: user_roles, order_items

Column Names

  • Use snake_case: first_name, created_at, is_active
  • Use descriptive names — created_at is better than ca or date1
  • Boolean columns should start with is_, has_, or can_: is_active, has_verified_email
  • Foreign keys should reference the parent table: user_id, order_id
  • Avoid reserved words as column names (order, group, select)

Aliases

  • Always use the AS keyword for column aliases: COUNT(*) AS total, not COUNT(*) total
  • Use meaningful table aliases: FROM orders o or FROM orders ord — not FROM orders x
  • Keep aliases short but recognizable — one to three characters that relate to the table name

Formatting SELECT Statements

The SELECT statement is the most common SQL operation. Here is the recommended format for a typical query:

SELECT
    u.id AS user_id,
    u.first_name,
    u.last_name,
    u.email,
    COUNT(o.id) AS order_count,
    SUM(o.total_amount) AS total_spent,
    MAX(o.order_date) AS last_order_date
FROM users u
LEFT JOIN orders o
    ON u.id = o.user_id
    AND o.status = 'completed'
WHERE u.is_active = 1
    AND u.created_at >= '2025-01-01'
GROUP BY
    u.id,
    u.first_name,
    u.last_name,
    u.email
HAVING COUNT(o.id) >= 3
ORDER BY total_spent DESC
LIMIT 50;
Enter fullscreen mode Exit fullscreen mode

Key points in this example: the SELECT list uses one column per line with consistent indentation; the JOIN condition is indented under the join; multiple WHERE conditions are aligned; and the GROUP BY columns are listed individually.

Formatting JOINs

JOIN formatting is critical because joins are where most query logic errors occur. Follow these rules:

  • Always specify the join type explicitly: INNER JOIN, LEFT JOIN, RIGHT JOIN, CROSS JOIN. Never use implicit joins with comma-separated tables in the FROM clause.
  • Place each JOIN on its own line, aligned with FROM.
  • Indent the ON clause under the JOIN.
  • For multi-condition joins, place each condition on its own line with AND aligned.
-- Good: explicit, well-formatted joins
SELECT
    c.name AS customer_name,
    p.name AS product_name,
    oi.quantity,
    oi.unit_price
FROM customers c
INNER JOIN orders o
    ON c.id = o.customer_id
INNER JOIN order_items oi
    ON o.id = oi.order_id
INNER JOIN products p
    ON oi.product_id = p.id
WHERE o.order_date >= '2026-01-01';

-- Bad: implicit join (hard to read, easy to create accidental cross joins)
SELECT c.name, p.name, oi.quantity, oi.unit_price
FROM customers c, orders o, order_items oi, products p
WHERE c.id = o.customer_id
AND o.id = oi.order_id
AND oi.product_id = p.id
AND o.order_date >= '2026-01-01';
Enter fullscreen mode Exit fullscreen mode

The explicit join syntax separates join conditions from filter conditions, making the query's intent much clearer.

Formatting WHERE Clauses

Complex WHERE clauses are a common source of bugs. Format them carefully:

SELECT *
FROM orders
WHERE status = 'shipped'
    AND total_amount > 50
    AND (
        shipping_country = 'US'
        OR shipping_country = 'CA'
    )
    AND order_date BETWEEN '2026-01-01' AND '2026-03-31';
Enter fullscreen mode Exit fullscreen mode

Rules for WHERE formatting:

  • Place each condition on its own line.
  • Start continuation lines with AND or OR, indented to align with the first condition.
  • Use parentheses to group OR conditions, and indent the contents of the parentheses.
  • Prefer IN over multiple OR conditions for the same column:
-- Better: use IN
WHERE shipping_country IN ('US', 'CA', 'MX')

-- Worse: multiple ORs
WHERE shipping_country = 'US'
    OR shipping_country = 'CA'
    OR shipping_country = 'MX'
Enter fullscreen mode Exit fullscreen mode

Formatting GROUP BY and ORDER BY

When grouping or ordering by multiple columns, list each column on its own line:

SELECT
    department,
    job_title,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary
FROM employees
WHERE is_active = 1
GROUP BY
    department,
    job_title
ORDER BY
    department ASC,
    avg_salary DESC;
Enter fullscreen mode Exit fullscreen mode

For simple single-column grouping or ordering, keeping it on one line is acceptable:

SELECT department, COUNT(*) AS cnt
FROM employees
GROUP BY department
ORDER BY cnt DESC;
Enter fullscreen mode Exit fullscreen mode

Formatting Subqueries

Subqueries should be indented and clearly delimited. Treat the subquery as a nested block:

SELECT
    u.first_name,
    u.last_name,
    u.email
FROM users u
WHERE u.id IN (
    SELECT DISTINCT o.user_id
    FROM orders o
    WHERE o.total_amount > 500
        AND o.order_date >= '2026-01-01'
)
ORDER BY u.last_name;
Enter fullscreen mode Exit fullscreen mode

For subqueries in the FROM clause, use a clear alias and indent the entire subquery:

SELECT
    top_customers.user_id,
    top_customers.total_spent,
    u.email
FROM (
    SELECT
        user_id,
        SUM(total_amount) AS total_spent
    FROM orders
    WHERE order_date >= '2025-01-01'
    GROUP BY user_id
    HAVING SUM(total_amount) > 1000
) top_customers
INNER JOIN users u
    ON top_customers.user_id = u.id
ORDER BY top_customers.total_spent DESC;
Enter fullscreen mode Exit fullscreen mode

However, if you find yourself nesting subqueries more than one level deep, consider refactoring to use CTEs (Common Table Expressions) instead.

Formatting CTEs (Common Table Expressions)

CTEs are one of the most powerful tools for writing readable SQL. They let you break complex queries into named, logical steps. Proper CTE formatting is essential:

WITH monthly_revenue AS (
    SELECT
        DATE_TRUNC('month', order_date) AS month,
        SUM(total_amount) AS revenue
    FROM orders
    WHERE status = 'completed'
    GROUP BY DATE_TRUNC('month', order_date)
),

revenue_growth AS (
    SELECT
        month,
        revenue,
        LAG(revenue) OVER (ORDER BY month) AS prev_month_revenue,
        ROUND(
            (revenue - LAG(revenue) OVER (ORDER BY month))
            / LAG(revenue) OVER (ORDER BY month) * 100,
            2
        ) AS growth_pct
    FROM monthly_revenue
)

SELECT
    month,
    revenue,
    prev_month_revenue,
    growth_pct
FROM revenue_growth
WHERE month >= '2025-07-01'
ORDER BY month;
Enter fullscreen mode Exit fullscreen mode

CTE formatting rules:

  • Place WITH on the first line, followed by the CTE name and AS (.
  • Indent the CTE body like a normal query.
  • Separate multiple CTEs with a comma and a blank line.
  • Give CTEs descriptive names that explain what they compute: monthly_revenue, active_users, revenue_growth.
  • Place the final SELECT after all CTEs, at the same indentation level as WITH.

CTEs make complex queries self-documenting. Instead of reading a nested mess of subqueries from the inside out, you read CTEs from top to bottom like a story.

Formatting INSERT, UPDATE, and DELETE

Data modification statements deserve the same formatting attention as queries:

INSERT

INSERT INTO users (
    first_name,
    last_name,
    email,
    is_active,
    created_at
)
VALUES (
    'Jane',
    'Doe',
    'jane.doe@example.com',
    1,
    CURRENT_TIMESTAMP
);
Enter fullscreen mode Exit fullscreen mode

UPDATE

UPDATE orders
SET
    status = 'cancelled',
    cancelled_at = CURRENT_TIMESTAMP,
    cancelled_by = 'system'
WHERE status = 'pending'
    AND created_at < '2026-01-01';
Enter fullscreen mode Exit fullscreen mode

DELETE

DELETE FROM session_tokens
WHERE expires_at < CURRENT_TIMESTAMP
    AND is_revoked = 1;
Enter fullscreen mode Exit fullscreen mode

Step-by-Step: Format SQL with DevToolkit SQL Formatter

You do not have to format SQL manually. The DevToolkit SQL Formatter handles it automatically. Here is how to use it:

Step 1: Open the Tool

Navigate to devtoolkit.cc/tools/sql-formatter. The tool loads instantly in your browser — no sign-up, no installation, no data sent to any server.

Step 2: Paste Your SQL

Paste your unformatted SQL into the input area. The formatter accepts any valid SQL, including queries with subqueries, CTEs, window functions, and complex joins. For example, paste this messy query:

select u.id,u.first_name,u.last_name,count(o.id) as order_count,sum(o.total_amount) as total_spent from users u left join orders o on u.id=o.user_id where u.is_active=1 and u.created_at>='2025-01-01' group by u.id,u.first_name,u.last_name having count(o.id)>=3 order by total_spent desc limit 50;
Enter fullscreen mode Exit fullscreen mode

Step 3: Click Format

Click the Format button. The tool instantly transforms your query into clean, properly indented SQL following industry-standard conventions:

SELECT
    u.id,
    u.first_name,
    u.last_name,
    COUNT(o.id) AS order_count,
    SUM(o.total_amount) AS total_spent
FROM users u
LEFT JOIN orders o
    ON u.id = o.user_id
WHERE u.is_active = 1
    AND u.created_at >= '2025-01-01'
GROUP BY
    u.id,
    u.first_name,
    u.last_name
HAVING COUNT(o.id) >= 3
ORDER BY total_spent DESC
LIMIT 50;
Enter fullscreen mode Exit fullscreen mode

Step 4: Copy and Use

Copy the formatted result and paste it into your codebase, documentation, or code review. The entire process takes less than five seconds.

The SQL Formatter is particularly useful when you inherit legacy code, review pull requests with unformatted SQL, or need to quickly clean up a query before sharing it with your team. You can also use our JSON Formatter for cleaning up JSON payloads that often accompany database work.

Establishing Team SQL Standards

Individual formatting discipline is good. Team-wide automated enforcement is better. Here are the most popular tools for enforcing SQL formatting standards:

SQLFluff

SQLFluff is the most popular open-source SQL linter and formatter. It supports multiple SQL dialects (PostgreSQL, MySQL, BigQuery, Snowflake, and more) and integrates into CI/CD pipelines:

# Install SQLFluff
pip install sqlfluff

# Lint a SQL file
sqlfluff lint query.sql

# Auto-fix formatting issues
sqlfluff fix query.sql

# Specify dialect
sqlfluff lint query.sql --dialect postgres
Enter fullscreen mode Exit fullscreen mode

SQLFluff has over 60 configurable rules. You can create a .sqlfluff configuration file in your repository to enforce your team's specific conventions:

[sqlfluff]
dialect = postgres
max_line_length = 120

[sqlfluff:indentation]
indent_unit = space
tab_space_size = 4

[sqlfluff:rules:capitalisation.keywords]
capitalisation_policy = upper

[sqlfluff:rules:capitalisation.functions]
capitalisation_policy = upper
Enter fullscreen mode Exit fullscreen mode

pgFormatter

pgFormatter is a PostgreSQL-specific formatter that focuses on producing clean, readable output. It is available as a command-line tool and as a web service:

# Install on macOS
brew install pgformatter

# Format a file
pg_format -s 4 -u 1 input.sql -o output.sql
Enter fullscreen mode Exit fullscreen mode

pgFormatter is simpler than SQLFluff but produces excellent results for PostgreSQL queries specifically.

Prettier with SQL Plugin

If your team already uses Prettier for JavaScript and TypeScript formatting, you can add SQL support with the prettier-plugin-sql package. This gives you a unified formatting pipeline across all languages in your codebase.

Editor Extensions

Most code editors have SQL formatting extensions: VS Code has "SQL Formatter" and "SQLTools", JetBrains IDEs have built-in SQL formatting, and Vim has plugins like "vim-sqlfmt". Configure your editor to format SQL on save for zero-effort consistency.

Comparison of SQL Formatting Tools

Here is how the most common SQL formatting approaches compare:

  • DevToolkit SQL Formatter — Best for quick, one-off formatting. Browser-based, free, no installation. Ideal when you need to format a query right now without setting up any tooling. All processing happens client-side so your SQL never leaves your machine.
  • SQLFluff — Best for CI/CD enforcement. Highly configurable, supports many dialects, catches logic issues beyond formatting. Requires Python installation and configuration. Best for teams that want automated enforcement in their deployment pipeline.
  • pgFormatter — Best for PostgreSQL-specific projects. Simple, fast, produces excellent output. Limited to PostgreSQL dialect.
  • Prettier SQL Plugin — Best for full-stack teams already using Prettier. Unified formatting pipeline across languages. Fewer SQL-specific configuration options than SQLFluff.
  • IDE Extensions — Best for individual developer productivity. Format-on-save integration. Configuration may not be shared across the team without additional setup.

For most teams, the ideal workflow combines an online tool like the DevToolkit SQL Formatter for quick formatting with a CI/CD linter like SQLFluff for automated enforcement.

Before and After: Real-World Examples

Example 1: E-Commerce Revenue Report

Before (unformatted):

select c.name as category,count(distinct o.id) as num_orders,count(distinct o.customer_id) as num_customers,sum(oi.quantity*oi.unit_price) as gross_revenue,sum(case when o.status='returned' then oi.quantity*oi.unit_price else 0 end) as returned_revenue,sum(oi.quantity*oi.unit_price)-sum(case when o.status='returned' then oi.quantity*oi.unit_price else 0 end) as net_revenue from categories c inner join products p on c.id=p.category_id inner join order_items oi on p.id=oi.product_id inner join orders o on oi.order_id=o.id where o.order_date between '2025-01-01' and '2025-12-31' group by c.name having sum(oi.quantity*oi.unit_price)>10000 order by net_revenue desc;
Enter fullscreen mode Exit fullscreen mode

After (formatted):

SELECT
    c.name AS category,
    COUNT(DISTINCT o.id) AS num_orders,
    COUNT(DISTINCT o.customer_id) AS num_customers,
    SUM(oi.quantity * oi.unit_price) AS gross_revenue,
    SUM(
        CASE
            WHEN o.status = 'returned'
            THEN oi.quantity * oi.unit_price
            ELSE 0
        END
    ) AS returned_revenue,
    SUM(oi.quantity * oi.unit_price) - SUM(
        CASE
            WHEN o.status = 'returned'
            THEN oi.quantity * oi.unit_price
            ELSE 0
        END
    ) AS net_revenue
FROM categories c
INNER JOIN products p
    ON c.id = p.category_id
INNER JOIN order_items oi
    ON p.id = oi.product_id
INNER JOIN orders o
    ON oi.order_id = o.id
WHERE o.order_date BETWEEN '2025-01-01' AND '2025-12-31'
GROUP BY c.name
HAVING SUM(oi.quantity * oi.unit_price) > 10000
ORDER BY net_revenue DESC;
Enter fullscreen mode Exit fullscreen mode

The formatted version is immediately readable. You can see the six calculated columns, the three joins, the date filter, and the revenue threshold at a glance.

Example 2: CTE-Based User Cohort Analysis

Before (unformatted):

with first_orders as (select user_id,min(order_date) as first_order_date from orders group by user_id),cohorts as (select user_id,date_trunc('month',first_order_date) as cohort_month from first_orders),activity as (select c.cohort_month,date_trunc('month',o.order_date) as activity_month,count(distinct o.user_id) as active_users from cohorts c inner join orders o on c.user_id=o.user_id group by c.cohort_month,date_trunc('month',o.order_date)) select cohort_month,activity_month,active_users,(select count(distinct user_id) from cohorts c2 where c2.cohort_month=a.cohort_month) as cohort_size,round(active_users::numeric/(select count(distinct user_id) from cohorts c2 where c2.cohort_month=a.cohort_month)*100,1) as retention_pct from activity a order by cohort_month,activity_month;
Enter fullscreen mode Exit fullscreen mode

After (formatted):

WITH first_orders AS (
    SELECT
        user_id,
        MIN(order_date) AS first_order_date
    FROM orders
    GROUP BY user_id
),

cohorts AS (
    SELECT
        user_id,
        DATE_TRUNC('month', first_order_date) AS cohort_month
    FROM first_orders
),

activity AS (
    SELECT
        c.cohort_month,
        DATE_TRUNC('month', o.order_date) AS activity_month,
        COUNT(DISTINCT o.user_id) AS active_users
    FROM cohorts c
    INNER JOIN orders o
        ON c.user_id = o.user_id
    GROUP BY
        c.cohort_month,
        DATE_TRUNC('month', o.order_date)
)

SELECT
    cohort_month,
    activity_month,
    active_users,
    (
        SELECT COUNT(DISTINCT user_id)
        FROM cohorts c2
        WHERE c2.cohort_month = a.cohort_month
    ) AS cohort_size,
    ROUND(
        active_users::NUMERIC / (
            SELECT COUNT(DISTINCT user_id)
            FROM cohorts c2
            WHERE c2.cohort_month = a.cohort_month
        ) * 100,
        1
    ) AS retention_pct
FROM activity a
ORDER BY
    cohort_month,
    activity_month;
Enter fullscreen mode Exit fullscreen mode

The CTE version reads like a pipeline: first compute first orders, then assign cohorts, then compute activity, and finally calculate retention. Each step is self-contained and testable in isolation.

Advanced Formatting Tips

CASE Expressions

Format CASE expressions with each WHEN/THEN on its own line, indented:

SELECT
    order_id,
    total_amount,
    CASE
        WHEN total_amount >= 1000 THEN 'high'
        WHEN total_amount >= 100 THEN 'medium'
        ELSE 'low'
    END AS order_tier
FROM orders;
Enter fullscreen mode Exit fullscreen mode

Window Functions

For complex window functions, break the OVER clause across multiple lines:

SELECT
    employee_id,
    department,
    salary,
    ROW_NUMBER() OVER (
        PARTITION BY department
        ORDER BY salary DESC
    ) AS salary_rank,
    salary - AVG(salary) OVER (
        PARTITION BY department
    ) AS salary_diff_from_avg
FROM employees;
Enter fullscreen mode Exit fullscreen mode

Long IN Lists

When filtering with a long list of values, place each value on its own line or group them logically:

WHERE country_code IN (
    'US', 'CA', 'MX',     -- North America
    'GB', 'DE', 'FR',     -- Europe
    'JP', 'KR', 'AU'      -- Asia-Pacific
)
Enter fullscreen mode Exit fullscreen mode

Comments

Use comments to explain the why, not the what. Place them above the clause they explain:

-- Exclude test accounts created by the QA team
WHERE u.email NOT LIKE '%@test.example.com'
    -- Only include users who completed onboarding
    AND u.onboarding_completed_at IS NOT NULL
Enter fullscreen mode Exit fullscreen mode

Frequently Asked Questions

Should SQL keywords be uppercase or lowercase?

The industry standard is uppercase keywords (SELECT, FROM, WHERE). This creates a clear visual distinction between SQL syntax and identifiers (table and column names). However, consistency matters more than the specific choice. If your entire team uses lowercase, that is acceptable — just do not mix styles.

Should I use tabs or spaces for SQL indentation?

Use spaces (4 spaces is the most common convention). Tabs render differently in different editors, terminals, and code review tools, which defeats the purpose of consistent formatting. Spaces ensure your SQL looks the same everywhere.

How do I format SQL automatically?

Use the DevToolkit SQL Formatter for instant browser-based formatting. For CI/CD automation, use SQLFluff or pgFormatter. For editor integration, install a SQL formatting extension and configure format-on-save.

Is there a standard SQL style guide?

There is no single official standard, but several widely adopted guides exist: Simon Holywell's SQL Style Guide, GitLab's SQL Style Guide, and Mozilla's SQL Style Guide. All of them agree on the fundamentals covered in this article: uppercase keywords, consistent indentation, one column per line, and explicit join syntax.

Should I use CTEs or subqueries?

Prefer CTEs for readability, especially when the query has multiple logical steps. CTEs are named, sequential, and testable in isolation. Use subqueries only for simple, single-use cases like WHERE id IN (SELECT ...). If you have nested subqueries more than one level deep, refactor to CTEs.

How long should a SQL query line be?

Keep lines under 120 characters. Most style guides recommend 80–120 characters as the maximum line length. If a line exceeds this limit, break it at a logical point (after a comma, before an operator, or at a clause boundary).

Does SQL formatting affect query performance?

No. SQL formatting is purely cosmetic — whitespace, line breaks, and keyword casing have zero impact on query execution. The database engine parses your query into an execution plan regardless of formatting. Format for humans, not for machines.

SQL Formatting Checklist

Use this checklist before committing any SQL to your codebase:

  • Keywords are consistently cased (preferably uppercase)
  • Each major clause starts on its own line (SELECT, FROM, WHERE, etc.)
  • Column lists use one column per line
  • Indentation is consistent (4 spaces)
  • Joins use explicit syntax (INNER JOIN, LEFT JOIN)
  • Join conditions are indented under the JOIN
  • WHERE conditions each get their own line
  • Parentheses are used to clarify AND/OR precedence
  • Table and column names use snake_case
  • Column aliases use AS
  • CTEs have descriptive names
  • No lines exceed 120 characters
  • Comments explain "why", not "what"

Start Formatting Your SQL Now

Clean SQL is not a luxury — it is a fundamental engineering practice that saves time, prevents bugs, and makes your entire team more productive. Whether you are writing a quick ad-hoc query or building a complex data pipeline, consistent formatting pays dividends every single day.

Ready to clean up your queries? Open the DevToolkit SQL Formatter and paste in your messiest SQL. In seconds, you will have perfectly formatted, readable queries that any developer on your team can understand at a glance. No sign-up, no installation — just clean SQL, instantly.

Top comments (0)