The cleanest way to format a SQL window function is to keep short OVER clauses on one line and break long ones — anything with both PARTITION BY and ORDER BY — across multiple indented lines. A window function packs partitioning, ordering, and an optional frame into a single OVER(...) expression, and when you jam all of that onto one line it becomes unreadable fast.
This guide gives you a consistent house style for the OVER clause: when to keep it inline, how to break PARTITION BY and ORDER BY onto their own lines, how to format window frames, and how to deduplicate repeated windows with a named WINDOW clause. Every example runs on PostgreSQL, MySQL 8+, and SQL Server.
Keep short OVER clauses on one line
If the OVER clause has just one sub-clause — only a PARTITION BY, or only an ORDER BY — leave it inline. Wrapping a trivial window across three lines adds noise without adding clarity.
Break long OVER clauses onto their own lines
Once a window has both PARTITION BY and ORDER BY, put the opening parenthesis on the function line, then each sub-clause on its own indented line, then close the paren at the base indent. This vertical layout makes the partitioning and ordering instantly scannable.
The same principle that makes JOINs and CTEs readable applies here: one logical clause per line, consistent indentation, closing punctuation aligned with the thing it opened.
Format window frames on their own line
A frame clause — ROWS or RANGE BETWEEN ... — is the third sub-clause and easily the longest. Always give it its own line below ORDER BY. Running totals and moving averages are where this pays off most.
Being explicit about the frame is also a correctness habit: with an ORDER BY but no frame clause, the default frame is RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, which can produce surprising results with duplicate ORDER BY values. Spelling out ROWS removes the ambiguity.
Deduplicate repeated windows with a named WINDOW clause
When several columns share the same OVER definition, repeating it is noisy and error-prone — change one copy and forget another, and your ranks silently disagree. Define the window once in a named WINDOW clause and reference it by name. It's supported in PostgreSQL, MySQL 8+, and SQL Server 2022+.
Now there's exactly one place to read or change the partition and ordering, and the SELECT list stays short.
Align column aliases and stack the SELECT list
Window functions live in the SELECT list, so the surrounding list formatting matters as much as the OVER clause. Put one expression per line and keep AS aliases readable. Trying to fit three windowed columns on one line undoes all the work of breaking the OVER clauses out.
If you also keep your aggregate-vs-window choices straight, you'll avoid the most common grouping mistake — see our guide to the "column must appear in the GROUP BY clause" error for when a window function is the right tool instead of GROUP BY.
Checklist: formatting a window function
One sub-clause (only PARTITION BY or only ORDER BY)? Keep OVER inline.
Two or more sub-clauses? Break the OVER clause across lines, one sub-clause per line.
Put PARTITION BY first, then ORDER BY, then the frame clause — always in that order.
Spell out the ROWS/RANGE frame explicitly instead of relying on the default.
Reused the same window 2+ times? Move it to a named WINDOW clause.
Keep one expression per line in the SELECT list and align the AS aliases.
Indent consistently — the same width everywhere — and align the closing paren with the function's line.
Wrapping up
Readable window functions come down to one rule with a few corollaries: short windows inline, long windows broken out one sub-clause per line, repeated windows named once. Applying it by hand across a big query is tedious, so sqlfmt does it automatically — it reflows OVER clauses, stacks the SELECT list, and keeps the indentation consistent across PostgreSQL, MySQL, SQLite, T-SQL, and BigQuery.