sql formatter tsql postgresql

SQL Formatter: Why Dialect Matters and How to Format Queries Properly

A practical guide to formatting SQL across T-SQL, PostgreSQL, MySQL, and SQLite — why dialect-aware formatting matters and how it fits into a real dev workflow.

· ByteKiln

Raw SQL is often painful to read. If you’ve ever received a stored procedure as a single wall of text, or tried to debug a dynamically constructed query from an ORM log, you know the feeling:

select u.id,u.name,u.email,o.total,o.created_at from users u inner join orders o on u.id=o.user_id where u.active=1 and o.total>100 order by o.created_at desc

A well-formatted equivalent is immediately understandable:

SELECT
  u.id,
  u.name,
  u.email,
  o.total,
  o.created_at
FROM
  users u
  INNER JOIN orders o ON u.id = o.user_id
WHERE
  u.active = 1
  AND o.total > 100
ORDER BY
  o.created_at DESC

Same query. Completely different reading experience. This guide covers what a good SQL formatter does, why dialect selection matters, and how to fit it into your actual workflow.


Why Dialect Matters

SQL is nominally standardized, but in practice every database engine has its own syntax quirks, reserved words, and formatting expectations. A formatter that doesn’t account for dialect will produce output that’s either wrong or looks alien to the team working on that database.

Here are some concrete examples of where dialects diverge:

Identifier quoting

  • T-SQL (SQL Server): Uses square brackets — [column_name]
  • MySQL: Uses backticks — `column_name`
  • PostgreSQL / SQLite / Standard SQL: Uses double quotes — "column_name"

If you’re formatting T-SQL with a MySQL-aware formatter, you get backticks everywhere — which is technically valid in some contexts but wrong for SQL Server.

Boolean literals

  • PostgreSQL / SQLite: TRUE and FALSE are keywords
  • MySQL: TRUE and FALSE work but are aliases for 1 and 0
  • T-SQL: No native boolean — uses 1 and 0, or BIT

String concatenation

  • T-SQL: first_name + ' ' + last_name
  • PostgreSQL / SQLite: first_name || ' ' || last_name
  • MySQL: CONCAT(first_name, ' ', last_name)

Top vs Limit vs Fetch

  • T-SQL: SELECT TOP 10 ...
  • MySQL / PostgreSQL / SQLite: SELECT ... LIMIT 10
  • Standard SQL 2008+: SELECT ... FETCH FIRST 10 ROWS ONLY

A formatter that knows your dialect will uppercase these correctly, preserve the syntax you intended, and not rewrite constructs that are valid in your target engine.


The Six Supported Dialects

The ByteKiln SQL formatter supports:

DialectCommon use
Standard SQLGeneric / cross-database formatting
MySQLMySQL and MariaDB projects
PostgreSQLPostgres-heavy backends, Supabase, Railway
T-SQLSQL Server, Azure SQL, SSMS
PL/SQLOracle database
SQLiteEmbedded databases, mobile, testing

Select the dialect that matches your target database. The formatting engine uses dialect-specific keyword sets and indentation conventions.


What Gets Formatted

A full-featured SQL formatter handles these structural elements:

KeywordsSELECT, FROM, WHERE, JOIN, GROUP BY, ORDER BY, etc. are uppercased and placed on their own lines or with consistent indentation.

Indentation — Nested subqueries, CTEs, and multi-level CASE expressions are indented proportionally to their nesting depth.

Comma placement — Leading vs trailing comma style is applied consistently. Many teams have a preference (leading commas make it easy to comment out a column without touching the previous line).

Clause spacing — Each major clause (WHERE, JOIN, HAVING) starts on its own line with consistent alignment.

Function calls — Preserved as-is but spaced correctly.


Reading the Size Stats

After formatting, the tool shows before/after character counts and the size change percentage. For most SQL, formatting increases size (whitespace is added). But this is useful to confirm the formatter didn’t silently drop content.

Where size stats are more directly useful: when you paste minified or aggressively compressed SQL from an ORM log or a monitoring tool, and you want to confirm the full statement was parsed and reproduced.


Practical Workflows

Reviewing a migration script

Migration scripts often accumulate formatting debt — different developers write in different styles. Before committing a migration, run it through the formatter to normalize style. The SQL is equivalent, but the diff in your version control will be easier for reviewers to read.

Debugging ORM-generated queries

ORMs like Entity Framework, Hibernate, or SQLAlchemy log queries in a compressed, often hard-to-read form. Copy the logged query, paste it into the formatter, select the appropriate dialect, and get a readable version. Now you can see what join is happening, whether the WHERE clause is what you expected, and whether an index-friendly pattern is being used. ORM queries often return JSON-typed columns alongside relational data — the JSON Formatter handles those payloads if you need to inspect them in the same session.

Writing ad-hoc queries

When writing complex queries by hand — especially those with CTEs, nested subqueries, or window functions — start formatted. Formatting helps you catch mismatched parentheses, incorrect join conditions, and missing clauses before you run the query.

Code reviews

Unformatted SQL in a code review is hard to read and easy to misread. If you receive a PR with raw, compressed SQL, format it locally to understand the logic before leaving a review comment.


Common SQL Formatting Patterns

CTEs (Common Table Expressions)

WITH
  recent_orders AS (
    SELECT
      user_id,
      SUM(total) AS total_spent
    FROM
      orders
    WHERE
      created_at > '2026-01-01'
    GROUP BY
      user_id
  )
SELECT
  u.name,
  ro.total_spent
FROM
  users u
  INNER JOIN recent_orders ro ON u.id = ro.user_id
WHERE
  ro.total_spent > 500
ORDER BY
  ro.total_spent DESC

Window functions

SELECT
  name,
  department,
  salary,
  RANK() OVER (
    PARTITION BY department
    ORDER BY salary DESC
  ) AS salary_rank
FROM
  employees

CASE expressions

SELECT
  order_id,
  CASE
    WHEN status = 'shipped' THEN 'In transit'
    WHEN status = 'delivered' THEN 'Complete'
    WHEN status = 'cancelled' THEN 'Cancelled'
    ELSE 'Pending'
  END AS status_label
FROM
  orders

Proper indentation of these structures makes the logic readable without comments.


What the Formatter Doesn’t Do

Execution — No database connection is made. The formatter works on text only. Your query is never run.

Semantic validation — It won’t tell you if a column doesn’t exist, if the JOIN condition is wrong, or if you have a logic error. For that, you need a real database.

Query optimization — Formatting doesn’t suggest indexes, rewrite inefficient patterns, or analyze query plans. It’s a presentation tool, not a query analyzer.


SQL Style Preferences

Formatting is partly about readability and partly about team conventions. Two common styles:

Trailing commas (common in many teams):

SELECT
  id,
  name,
  email

Leading commas (easy to comment out a column):

SELECT
  id
  , name
  , email

Neither is wrong. The value of a formatter is picking one and applying it everywhere.


If you’re working with DDL rather than queries, the JSON to SQL guide covers inferring table schemas from JSON objects, and the C# to SQL guide handles the entity class → CREATE TABLE translation — both produce output that’s worth running through the formatter before committing.

The SQL formatter’s job is simple but high-value: take any SQL regardless of how it arrived, and make it readable in the context of the database you’re targeting. Paste it in, pick your dialect, and move on.