You inherit a stored procedure from a former colleague. The whole thing is one 800-line SELECT-to-INSERT rope jammed onto four lines. Your job is to find the logic bug. Your actual job is to format it into something readable first.
Paste any SQL in here. Choose a dialect (Postgres, MySQL, SQL Server, BigQuery, Snowflake, SQLite, Oracle, Db2, MariaDB, CockroachDB, plus a generic ANSI fallback). The formatter parses the AST -- so it actually understands the query, instead of just regexing keywords -- and emits consistently cased, indented SQL.
A before/after that shows why this matters:
-- Before (mystery meat from prod):
select u.id,u.name,count(o.id) as order_count from users u left join orders o on u.id=o.user_id where u.created_at>'2024-01-01' and u.status in ('active','trial') group by u.id having count(o.id)>5 order by order_count desc limit 100;
-- After (same query, now legible):
SELECT
u.id,
u.name,
COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
AND u.status IN ('active', 'trial')
GROUP BY u.id
HAVING COUNT(o.id) > 5
ORDER BY order_count DESC
LIMIT 100;
The gotcha that makes formatters round-trip incorrectly: string literals that contain reserved words.
A clause like WHERE sentence LIKE '% FROM THE %' looks to a regex-based formatter like a FROM keyword in the middle of a string, and it inserts a newline. That is still valid SQL in most dialects, but in cases like WHERE x = 'SELECT' /* comment */, a naive formatter will try to uncomment the block-comment from inside the string. The AST-based approach here correctly tokenizes string literals and leaves their insides alone.
Other things worth knowing:
-
Comma style -- trailing (
col1,) versus leading (,col1) for column lists. Leading is the move forSELECTlists you edit frequently; diffs stay clean when columns get added or removed. Both are supported. -
Keyword casing --
SELECT,from, orselect. Convention is uppercase keywords, lowercase identifiers. Pick your camp; the formatter applies it uniformly. -
Nested subqueries and CTEs get their own indentation level. A
WITH a AS (...), b AS (...) SELECT ...chain stays readable. -
Comments (
--and/* */) travel with the statement they belong to. A comment betweenSELECTand the first column stays at the top, not pushed into the column list. -
DDL inside procedural blocks (PL/pgSQL, T-SQL, PL/SQL) -- the formatter parses procedural constructs and applies consistent indentation inside
BEGIN ... END,IF ... END IF,LOOP ... END LOOP.
Stick the formatter on a pre-commit hook. Nobody has to argue about tabs-versus-spaces in SQL ever again.