For SQL Server Management Studio 21 / 22

SSMS SQL Formatter

User Guide & Reference — rule-based and AI-assisted T-SQL formatting, comment handling, SELECT * expansion, and Excel/CSV export for SQL Server Management Studio.
Version 2.8.1  •  Updated 2026-08-20  •  Made by DesignAlign
Download the latest release ⇩ Download this guide as PDF ← Back to the product page

Contents

1. Quick reference3
2. Getting started4
3. Formatting engines4
4. Style presets & basics5
5. Line breaks, lists & alignment6
6. Blank lines & GO7
7. Comment handling7
8. Expand SELECT *8
9. Annotate Script8
10. Format on save & format on paste9
11. Batch formatting & the CLI9
12. Shared team config10
13. Exporting results to Excel & CSV10
14. AI Engine settings11
15. Export / Import Formatter Settings12
16. Tips & troubleshooting12

1. Quick reference

The extension only defines 4 global keyboard shortcuts — everything else lives in the Tools menu (or right-click in the editor for Format T-SQL Script). A dedicated SQL Formatter toolbar with the same six most-used commands (Format, Preview, Expand SELECT *, Export to Excel, Annotate Script, Remove Annotations) also docks automatically the first time the extension loads.

ShortcutAction
Ctrl+Shift+Alt+FFormat T-SQL Script
Ctrl+Shift+Alt+PPreview Format
Ctrl+Shift+Alt+XExport Results to Excel
Ctrl+Shift+Alt+AAdd Results as Sheet
Tools menu (no shortcut)Action
Format T-SQL Script HelpUsage summary + link to docs, shown inside SSMS
Open Results in ExcelWrite copied results to a temp workbook and open it
Expand SELECT *Resolve SELECT * to explicit columns, with preview
Annotate ScriptInsert schema/documentation/relationship comments — comments only, never changes query logic
Remove AnnotationsStrip every comment previously inserted by Annotate Script
Format Files…Format one or more .sql files on disk
Format All Open FilesFormat every open .sql document, one undo per window
Export / Import Formatter SettingsSave or load the whole configuration as JSON

With text selected, Format T-SQL Script and Preview Format apply to the selection only; otherwise the whole document. Ctrl+Z always undoes a format as one step.

2. Getting started

SSMS doesn't expose the Visual Studio Marketplace in its own extension manager, so installation is manual: open the SSMS SQL Formatter Marketplace listing, select Download, then double-click SsmsSqlFormatter.vsix (the installer should list "SQL Server Management Studio 21/22" as a target — tick it and install) and restart SSMS. If the installer doesn't offer SSMS as a target, run VSIXInstaller.exe directly from the SSMS installation folder against the downloaded VSIX.

Once installed, open a query window and press Ctrl+Shift+Alt+F, right-click → Format T-SQL Script, or use the Tools menu. With text selected, only the selection is formatted; otherwise the whole document. The change lands as a single undo step, so Ctrl+Z reverts the entire format in one press.

A script that fails to parse is always left completely untouched — you'll see the parse error with line/column instead of a partially-mangled file. This applies everywhere: the main command, format-on-save, format-on-paste, batch formatting, and the CLI.

All settings live under Tools → Options → Format T-SQL Script (General and AI Engine pages). A Format T-SQL Script Help entry in the Tools menu shows a quick usage summary without leaving SSMS.

Microsoft does not officially support extensions in SSMS 21/22 — they're tolerated and widely used, but if SSMS misbehaves with an extension installed, Microsoft won't help. Keep the VSIX handy so you can uninstall via VSIXInstaller if needed.

3. Formatting engines

Set under Tools → Options → General → Formatting engine. Everything except the interactive Format T-SQL Script command (and Expand SELECT *) always uses Rule-based, regardless of this setting.

EngineHow it worksStrengthsTrade-offs
Rule-basedMicrosoft's ScriptDom parser + script generator, fully offlineInstant, deterministic, understands all T-SQLRegenerating from the parse tree loses comments on its own — see Comment handling
AIAnthropic Claude Messages API, or GitHub Copilot / any OpenAI-compatible chat endpoint, using your own key or tokenPreserves comments exactly, follows free-form style instructions ("leading commas", "align equals signs")Needs network + a key/token; script text is sent to the provider

Two built-in style presets apply to the Rule-based engine (set under Style preset): Classic (old-format compact — trailing commas, inline JOINs, fewer line breaks) and Modern (each column/JOIN/predicate on its own line, aligned bodies). Choose Custom to control every individual option yourself — see the next section.

4. Style presets & basics

These live in Tools → Options → General, category 02. Basics. They only take effect when Style preset is Custom — Classic and Modern use their own built-in combinations.

OptionWhat it does
Keyword casingUppercase, Lowercase, or PascalCase for T-SQL keywords.
Built-in function casingCasing for built-in function names (GETDATE, COUNT, ISNULL…). Only names immediately followed by '(' are affected, so a column sharing a function's name is left alone.
Data type casingCasing for data type keywords (INT, VARCHAR, DATETIME…).
Indent size (spaces)Default 4.
Indent with tabsConverts each indent level into a tab character.
Re-indent subqueriesGuarantees every nested subquery/derived table is indented at least one level per nesting depth. Only ever adds indentation.
Comma placementTrailing (end of line) or Leading (start of next line — handy for easy column commenting). Applies to every preset.
Add semicolonsTerminate statements with semicolons.
Max line length (0 = off)Wraps a top-level comma-separated list (SELECT/GROUP BY/ORDER BY) one item per line once it exceeds this many characters. Doesn't apply inside parentheses (IN-lists, function arguments).
AS keyword on its own linePlaces AS on a new line in views/procedures (Custom preset only).

5. Line breaks, lists & alignment

03. Line breaks (Custom preset only)

Each of these independently controls whether that clause starts on a new line: New line before FROM / WHERE / JOIN / GROUP BY / ORDER BY / HAVING / OUTPUT / OFFSET, plus New line before ( / ) for the opening and closing parenthesis of a multiline list. All default to on.

04. Lists

OptionWhat it does
Multiline SELECT listEach selected column on its own line.
Multiline WHERE predicatesEach AND/OR predicate on its own line.
Multiline INSERT listsSame, for INSERT column/value lists.
Align clause bodiesAligns the body of clauses (SELECT list, SET clauses, etc.).
Align column definitionsAligns column definition fields in CREATE TABLE.
Multiline view columnsEach column in a view's column list on its own line.
Indent view body / Indent SET clauseIndents the body of a view, or an UPDATE statement's SET clause.
Align '=' in assignments (off by default)In runs of 2+ consecutive "name = expression" lines (SET clauses, old-style SELECT aliasing), pads the shorter left-hand sides so every '=' lines up. A line with more than one top-level '=' (a comparison, not an assignment) breaks the run and is left alone.
Align ON in JOIN clauses (off by default)Condenses each JOIN onto one line ("JOIN table ON condition" — ScriptDom's generator otherwise always splits this across three lines) and, in runs of 2+ consecutive joins, pads the shorter ones so every ON lines up.
Align THEN in CASE expressions (off by default)Expands a CASE with 2+ WHEN branches onto multiple lines (ScriptDom never does this on its own) and pads the shorter conditions so every THEN lines up. A single-branch CASE is left untouched.

6. Blank lines & GO

Category 05. Blank lines and GO. Every count below accepts -1 to mean "leave as-is."

OptionWhat it does
Blank lines before / after GOExact blank-line count around each GO batch separator. Set both to -1 to leave GO spacing untouched entirely.
Blank lines between statementsExact blank-line count between consecutive statements, at every nesting level — top-level batch statements and statements inside BEGIN…END, IF/WHILE, TRY/CATCH, and procedure/function/trigger bodies. A comment above a statement stays attached to it (blank lines go above the comment, not between it and the statement).
Max consecutive blank linesCollapses runs of blank lines anywhere down to this many (-1 = unlimited). Never touches blank lines inside /* */ comments; the GO settings above take precedence around GO.
Trim trailing whitespaceRemoves spaces/tabs at line ends.

7. Comment handling

The rule-based engine regenerates the script from its parse tree, which doesn't retain comments on its own — Comment handling (category 06. Safety) controls what happens to them instead. Comments are never silently deleted by default.

ModeBehavior
Inline (default)Re-attaches each comment to the same code it was next to — trailing comments stay at line ends, standalone comments keep their own line. If a comment can't be confidently repositioned, it's appended at the end under a banner instead of being dropped.
MoveToEndStrips every comment out of its original position and collects them all at the end of the script, in their original order.
DiscardDrops comments entirely. Warn when script contains comments (on by default) shows a confirmation before this happens.

The AI engine always preserves comments exactly, regardless of this setting. A separate Enable formatting cache option (off by default, also under 06. Safety) caches identical script+settings combinations in memory to speed up repeated formatting.

8. Expand SELECT *

Replaces SELECT * (and alias.*) with an explicit, ordered column list resolved from the real table/view structure — using whatever database the active query window is already connected to, so there's nothing extra to configure. Anywhere it can't confidently resolve the source, it leaves that SELECT * untouched, decided independently for each occurrence in the script — it never guesses.

Two ways to use it

Requires the query window to actually be connected to a database. If no connection can be determined, the command tells you so and nothing changes.

What it can and can't resolve

Resolves plain base tables and views reachable through the connection — including old-style system compatibility views like sysobjects, modern catalog views/DMVs like sys.all_columns or sys.dm_exec_sessions, and INFORMATION_SCHEMA views like INFORMATION_SCHEMA.COLUMNS — and four-part linked-server references (LinkedServer.Database.Schema.Table), by querying the linked server's own catalog directly. That only works when the linked server is itself SQL Server; anything else (Oracle, ODBC, flat file, ...) fails the same lookup and is left untouched, same as any other unresolvable table. It does not resolve: CTEs, derived tables/subqueries, table-valued functions, temp tables, table variables, or synonyms — any of these are simply left as SELECT *. Windows/Integrated Authentication connections are the reliable case; SQL Server Authentication connections may not always be resolvable.

Interactive only — this never runs on format-on-save, format-on-paste, batch formatting, or the CLI, since it requires a live database connection.

9. Annotate Script

Inserts /* ... */ comments describing columns referenced in SELECT lists, JOIN conditions, and WHERE clauses — data type and nullability, documentation recorded via sp_addextendedproperty, foreign key targets, sensitivity/PII classification, encryption/masking flags, CHECK constraint text, computed-column formulas, and inline discrepancy warnings (see below) — plus approximate row counts on joined tables, a banner listing child tables before a DELETE, and a syntax-error banner when the script doesn't parse at all. Comments only — this never changes a single line of query logic. Same connection requirement and "never guess" philosophy as Expand SELECT *: anything it can't confidently resolve is left completely untouched.

Two ways to use it

Requires the query window to actually be connected to a database. If no connection can be determined, the command tells you so and nothing changes.

What it can and can't resolve

Resolves the same range of tables Expand SELECT * does — plain base tables/views, old-style system compatibility views like sysobjects, and modern catalog views/DMVs/INFORMATION_SCHEMA views — though for catalog views and DMVs specifically, only basic type and nullability facts are available, since there's no local object_id to hang documentation, CHECK constraints, or FK info off of for those. CTEs, derived tables, table-valued functions, temp tables, table variables, synonyms, and ambiguous unqualified column names are left untouched. Sensitivity classification requires SQL Server 2019+, and encryption/masking indicators require SQL Server 2016–2017+; on an older server those specific fields are simply omitted rather than failing the whole annotation.

Discrepancy warnings

Flags likely bugs and script-vs-schema mismatches right next to the column or comparison they're about: = NULL / <> NULL (always evaluates to UNKNOWN in T-SQL's three-valued logic, never a match — almost always meant as IS [NOT] NULL, and flagged regardless of whether the other side resolves to a known column), a column compared directly against a literal of a contradicting type (e.g. an int column compared to a string literal), and an IS [NOT] NULL check against a column the schema says is actually NOT NULL — a check that can then only ever go one way.

Syntax errors

If the script doesn't parse at all, Annotate Script no longer does nothing silently. It appends one comment below the script — which stays completely untouched otherwise — stating the line, column, and message for every error the formatter's parser reported, plus the actual offending source line, so the problem is identifiable at a glance without switching back to SSMS's own error output. This also catches a sneakier case that the underlying parser doesn't flag on its own: an unclosed [ or " that happens to get accidentally "closed" by a later, unrelated bracket or quote (e.g. SELECT [Foo, [Bar] FROM dbo.Widgets) — rather than silently producing a bizarre identifier with no error at all, it's flagged the same way as a genuine syntax error.

Safety and re-running

Every annotation is a block comment (/* ... */), never a line comment (--) — a line comment would silently swallow everything after it on the same line, which for a JOIN or WHERE column that isn't the last token on its line would delete real logic. A collation-conflict warning only suggests a COLLATE fix as comment text; it's never actually inserted into the query. Running Annotate Script again on an already-annotated script refreshes its annotations in place rather than stacking duplicates — this works because each comment carries a recognizable [Note] label ahead of its content, so a later run can tell its own prior comments apart from anything hand-written.

Configure what's included

Six toggles under Tools → Options → General (category 12. Annotate Script, all on by default): Include schema info, Include documentation, Include relationships, Include security, Include quality warnings, and Include discrepancies. Show syntax errors (on by default) controls the syntax-error banner above. Turning off Include security is worth considering if the annotated script might end up shared or committed somewhere you don't want data-sensitivity labels to appear.

Connection-info footer

Include connection info (on by default) appends one comment below the script — regardless of whether anything else in the script was annotatable — noting the connection's server and database name, the SQL Server version/edition, when the annotation run happened, who ran it (Windows username and machine name), and the extension's own version. Even a script where every table is unresolvable still gets this footer on its own, so you always know which connection a script was checked against.

Customizing or hiding the label

Show '[Note]' label in comments (on by default) controls whether inserted comments carry that recognizable label at all, and Custom label text lets you replace [Note] with your own text (e.g. a team name). Turning the label off is a real tradeoff, not just a cosmetic one: with no label, a previous run's comments look exactly like hand-written ones, so Annotate Script can no longer find and refresh them — re-running then always adds a fresh set of comments alongside whatever's already there instead of replacing it in place.

Warning: the label is also how Remove Annotations (below) finds comments to strip. Annotations added while Show '[Note]' label in comments is off, or while Custom label text is blank, have nothing recognizable to search by — Remove Annotations can never find or remove them, no matter what the settings are changed back to afterward. The extension warns about this both when you annotate under those settings and when a Remove Annotations run comes back empty.

Removing annotations

Tools → Remove Annotations (or its own toolbar button) strips every comment Annotate Script previously inserted, identified the same way the re-run refresh finds them — by the configured label text, regardless of whether Show '[Note]' label in comments happens to be on or off right now. Unlike Annotate Script itself, this is a plain local text operation with no connection required, and applies as a single undo step.

Annotate Script is interactive only — it never runs on format-on-save, format-on-paste, batch formatting, or the CLI, since it requires a live database connection. Remove Annotations has no such requirement, but is likewise only available interactively.

10. Format on save & format on paste

Format on save

Automatically formats a .sql document immediately before it's written to disk (Ctrl+S, Save All, closing a dirty document, etc.), using your General settings. Always uses the Rule-based engine — never AI — so saving is never delayed by a network call or a confirmation prompt. A script that fails to parse is saved untouched.

Format on paste

Automatically reformats a .sql document right after pasting multi-line text into it. Detected as a single edit that inserts multi-line text, so ordinary typing is never affected. Same Rule-based-only, parse-failures-left-untouched behavior as format on save.

Both are off by default — turn them on under Tools → Options → General (category 09. Format on save).

11. Batch formatting & the CLI

Format Files…

Tools menu → pick one or more .sql files and format them on disk in place (Rule-based engine, original file encoding preserved). A file that fails to parse is left untouched and reported at the end.

Format All Open Files

Formats every currently-open .sql document in place. Each is a normal editor edit — Ctrl+Z in that window undoes it — and nothing is written to disk unless you save afterward.

Command line (CI)

tools/SsmsSqlFormatter.Cli builds a standalone ssmssqlfmt.exe — Rule-based engine only, no AI, no SSMS or Visual Studio install required — for gating a build or PR on "SQL is formatted":

ssmssqlfmt check <file-or-directory...> [--config settings.json]   # reports what would change, exits 1 if anything would
ssmssqlfmt format <file-or-directory...> [--config settings.json]  # formats in place, same as Format Files...

A directory argument is searched recursively for *.sql files. --config takes the same JSON produced by Export Formatter Settings, so a team can share one style file between the IDE and CI. Example GitHub Actions step:

- name: Check SQL formatting
  run: tools\SsmsSqlFormatter.Cli\bin\Release\net48\ssmssqlfmt.exe check sql\ --config .sqlformatter.json

12. Shared team config

Drop a .sqlformatter.json file (same format produced by Export Formatter Settings) into a repo folder, and every format of a file under that folder — or a descendant folder — picks it up automatically, both in SSMS and from the CLI. It overrides just that one operation; your own Tools > Options settings are never modified or persisted. If the file is missing or fails to parse, formatting silently falls back to your own settings rather than blocking.

Turn this off with Use folder-level .sqlformatter.json under Tools → Options → General (category 10. Shared config, on by default) if you don't want a folder's contents to ever affect formatting.

13. Exporting results to Excel & CSV

Click in the results grid, select cells (Ctrl+A for all), press Ctrl+Shift+Alt+X, then Ctrl+V in Excel — you get a real table with bold headers and, by default, every cell as Text so leading zeros and long IDs survive. Add Results as Sheet (Ctrl+Shift+Alt+A) queues additional result sets into the same workbook before exporting.

Cell text is sanitized against characters XML 1.0 doesn't allow in text content — an occasional stray control character in varchar/text column data, for instance — before it's written into the .xlsx, so a result set containing one still opens cleanly in Excel instead of triggering its "we found a problem with some content" repair prompt.

SSMS's results grid builds its own private context menu, so right-clicking it won't show the extension's commands. Use the keyboard shortcut, the Tools menu, or add a toolbar button (Tools → Customize → Commands → Toolbar → Standard → Add Command → Tools → Copy Results as Excel Table) — a toolbar button is always visible and survives upgrades.

07. Export results to Excel

OptionWhat it does
Export formatXlsx (styled workbook) or Csv (styling options below don't apply to Csv).
Output folderLeave empty to use the Windows temp folder. Created if it doesn't exist; falls back to temp if it can't be used.
Ask where to saveShows a Save As dialog per export instead of writing straight to the output folder.
Include query on a separate sheetAdds a "Query" worksheet with the SQL that produced the data (Xlsx only).
Try to copy the grid automaticallySends the grid's Copy-with-Headers keystroke before converting — only works when the grid still has focus, i.e. via the Ctrl+Shift+Alt+X shortcut. A toolbar/menu click moves focus away, so whatever you copied yourself is converted instead.
First row contains headersFormats the first copied row as a bold header. Turn off if you copied without headers.
Paste all cells as textKeeps leading zeros and prevents long numbers turning into scientific notation or ID-like values into dates. Disable to let Excel auto-detect types.
Paste NULL as empty cellsConverts the grid's literal "NULL" text into empty cells.

08. Excel appearance

OptionWhat it does
Font name / sizeApplied to the exported workbook.
Bold header row 
Header background / text colourColour picker via the dropdown.
Show cell borders / Border colour 
Freeze header rowKeeps headers visible while scrolling long result sets.
Add AutoFilter to headersAdds Excel filter dropdowns to the header row.
Banded rows / Band colourShades every second data row for readability (off by default).

14. AI Engine settings

Configured on the separate Tools → Options → AI Engine page.

1. Connection

OptionWhat it does
ProviderAnthropic (Messages API) or Copilot (Copilot-style chat endpoint).
API key / tokenYour Anthropic API key or Copilot bearer token. Stored in Windows Credential Manager for the current user — never in plain text.
Modele.g. claude-sonnet-4-5 (Anthropic) or a chat model your Copilot-compatible endpoint supports.
API endpointDefaults to https://api.anthropic.com/v1/messages; point this at your Copilot-compatible chat completions endpoint (or any OpenAI-compatible endpoint returning a choices[].message.content shape) to switch providers.
Max output tokens / TimeoutRaise both for very large scripts.

2. Behaviour

OptionWhat it does
Custom style instructionsFree-form text appended to the prompt, e.g. "leading commas", "align equals signs in SET clauses", "keep short CASE expressions on one line".
Send General options as style guideTranslates your rule-based settings (casing, indent, line breaks) into the AI prompt so both engines produce a consistent style.
Fall back to rule-based on errorIf the AI call fails (no network, bad key, timeout), silently formats with the rule-based engine instead.

3. Privacy

Confirm before sending script (on by default) asks for confirmation before sending SQL to the provider — your script text, including any embedded literals or data, leaves the machine when using the AI engine. Get an Anthropic key at console.anthropic.com (billed separately from any Claude.ai subscription).

15. Export / Import Formatter Settings

Export Formatter Settings (Tools menu) saves your current configuration to a JSON file — the same format read by Import Formatter Settings and by .sqlformatter.json / the CLI's --config. Use it to share one style across a team, or to keep a personal backup before experimenting with settings.

16. Tips & troubleshooting

QuestionAnswer
Format T-SQL Script doesn't appear when I right-clickSome SSMS builds don't add it to the query editor's private context menu automatically. The keyboard shortcut and Tools menu always work; to add the context-menu entry yourself (one-time, survives upgrades): Tools → Customize → Commands tab → Context menu → "Editor Context Menus | Code Window" → Add Command → category Tools → Format T-SQL Script → OK → Close.
Right-clicking the results grid shows no export optionThe grid builds its own private context menu. Use Ctrl+Shift+Alt+X, the Tools menu, or add a toolbar button — see Exporting results to Excel & CSV, above.
My comments moved or disappearedCheck Comment handling under Tools > Options > General — Discard mode drops them (with a warning first). Inline or MoveToEnd never drop a comment; switch to the AI engine if you need them to stay in their exact original spot.
Expand SELECT * isn't finding my tableIt only resolves plain base tables/views reachable through the active connection (including linked-server tables, when the linked server is itself SQL Server) — not CTEs, derived tables, table-valued functions, temp tables, synonyms, or non-SQL-Server linked servers. Confirm the query window shows a live connection, and that the table/linked-server name and schema are spelled correctly.
Annotate Script isn't annotating a columnSame resolution rules as Expand SELECT * — the table must be a plain base table/view reachable through the active connection, and an unqualified column name must match exactly one table in scope (an ambiguous match across joined tables is left alone). Some categories (sensitivity classification, masking, Always Encrypted) also depend on the connected SQL Server's version.
A script wasn't touched at allBy design — a script that fails to parse is always left completely untouched. You'll see the parse error with line/column instead.
Tools > Options shows an error, or won't load, right after upgradingAn in-place VSIX upgrade can occasionally leave a stale extension cache behind. Close SSMS and run tools/ssms-diagnose-and-reset.ps1 (included in the project source) — it prints the relevant SSMS activity log entries, uninstalls the extension, and clears ComponentModelCache (SSMS rebuilds it automatically). Reinstall the latest VSIX afterwards, or pass -VsixPath to have the script do that in the same run.
How do I uninstall?Run tools/ssms-diagnose-and-reset.ps1 (see above), or manually: VSIXInstaller.exe /uninstall:SsmsSqlFormatter.7f3d2a1e-5b8c-4e9f-a6d0-1c4b7e2f9a35