T-SQL Formatter for SQL Server

Paste Transact-SQL from SSMS, a stored procedure or a deployment script, choose T-SQL (SQL Server) and press Ctrl/Cmd+Enter to lay it out.

Input

Settings

History

Load from URL

What the T-SQL dialect recognises

SQL Server syntax trips up generic formatters mostly at the token level. With T-SQL selected:

  • Square brackets quote identifiers, so [Order Details], [Company Name] and keyword-named columns like [Order] are kept as single names. A ] inside a name is escaped as ]].
  • Double quotes are identifiers too, matching the default QUOTED_IDENTIFIER ON setting.
  • N’…’ strings hold Unicode text such as N'Österreich'. Quotes inside a string are doubled; a backslash has no special meaning.
  • Variables keep their prefix: @CustomerID, @@ROWCOUNT, and temp tables #recent and ##staging are names, not comments.
  • Block comments nest, as they do in SQL Server.
  • ODBC escapes like {fn curdate()} and {d '2026-01-01'} pass through untouched.

Pick the wrong dialect and the damage can be silent. Under PostgreSQL, [Order Total] is not seen as a quoted name, and the formatter turns it into [ORDER Total]. Under MySQL or Standard SQL the bracket is rejected outright.

SQL Server clauses and their layout

TOP (10) stays in front of the first column on the line after SELECT. CROSS APPLY and OUTER APPLY are laid out like joins, with the correlated subquery indented beneath them, which makes the “latest N rows per customer” pattern easy to follow. OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY is split into its two parts. A MERGE gets one line per WHEN MATCHED / WHEN NOT MATCHED BY TARGET / BY SOURCE branch, and PIVOT (…) stays attached to its derived table.

Some constructs are not understood as clauses. An OUTPUT inserted.Sku, deleted.Quantity list is folded into the preceding SET or column list rather than given its own line. A table hint such as WITH (NOLOCK) is placed on a separate WITH line that looks like the start of a CTE. Both stay valid T-SQL; adjust them by hand if your team reviews the layout.

GO, procedures and control flow

GO is a batch separator understood by SSMS and sqlcmd rather than by the server. It stays on its own line when the next batch starts with SELECT, INSERT, CREATE, SET or USE, and Minify keeps it on a line of its own as well. When a batch begins with DECLARE, PRINT or EXEC, that statement is pulled up onto the GO line. Check those spots before running the output in sqlcmd.

Procedural code is formatted statement by statement. In a CREATE PROCEDURE, the queries inside BEGIN … END are laid out well, but the parameter list runs on into AS BEGIN. SET NOCOUNT ON is split across two lines, and BEGIN TRY/BEGIN CATCH share a line with their first statement. Nothing is dropped or reordered, so the procedure still compiles.

Settings for SQL Server code

SQL Server teams often write PascalCase names with upper-case keywords. The default Keyword case: UPPER fits that, and Function case: UPPER turns sum and dateadd into SUM and DATEADD. Commas → Leading is a popular T-SQL convention and works inside MERGE update lists. Use the SQL validator to check bracket and quote balance in a long script before deployment.

Examples

Top customers by order value

Bracketed names with spaces, an N string and TOP (10) on a classic Northwind-style report.

Input
select top (10) [o].[OrderID], [c].[Company Name], sum([od].[Quantity] * [od].[UnitPrice]) as [Order Total] from dbo.Orders o inner join dbo.Customers c on c.CustomerID = o.CustomerID join dbo.[Order Details] od on od.OrderID = o.OrderID where o.OrderDate >= '20260101' and c.Country = N'Österreich' group by [o].[OrderID], [c].[Company Name] order by [Order Total] desc;
Output
SELECT
  TOP (10) [o].[OrderID],
  [c].[Company Name],
  SUM([od].[Quantity] * [od].[UnitPrice]) AS [Order Total]
FROM
  dbo.Orders o
  INNER JOIN dbo.Customers c ON c.CustomerID = o.CustomerID
  JOIN dbo.[Order Details] od ON od.OrderID = o.OrderID
WHERE
  o.OrderDate >= '20260101'
  AND c.Country = N'Österreich'
GROUP BY
  [o].[OrderID],
  [c].[Company Name]
ORDER BY
  [Order Total] DESC;
Open this example in the tool

Latest three orders per customer with CROSS APPLY

CROSS APPLY is laid out like a join with its correlated subquery indented underneath.

Input
select c.CustomerID, c.Name, lo.OrderID, lo.OrderDate from dbo.Customers c cross apply (select top 3 o.OrderID, o.OrderDate from dbo.Orders o where o.CustomerID = c.CustomerID order by o.OrderDate desc) lo;
Output
SELECT
  c.CustomerID,
  c.Name,
  lo.OrderID,
  lo.OrderDate
FROM
  dbo.Customers c
  CROSS APPLY (
    SELECT
      TOP 3 o.OrderID,
      o.OrderDate
    FROM
      dbo.Orders o
    WHERE
      o.CustomerID = c.CustomerID
    ORDER BY
      o.OrderDate DESC
  ) lo;
Open this example in the tool

Deployment script with GO batches

Each GO stays on a line of its own between the CREATE TABLE batch and the INSERT batch.

Input
create table dbo.AuditLog (Id int identity(1,1) primary key, Event nvarchar(200) not null, CreatedAt datetime2 not null default sysutcdatetime())
GO
insert into dbo.AuditLog (Event) values (N'schema created');
select @@rowcount as Inserted;
GO
Output
CREATE TABLE dbo.AuditLog (
  Id INT identity(1, 1) PRIMARY KEY,
  Event NVARCHAR(200) NOT NULL,
  CreatedAt DATETIME2 NOT NULL DEFAULT sysutcdatetime()
)
GO
INSERT INTO
  dbo.AuditLog (Event)
VALUES
  (N'schema created');

SELECT
  @@rowcount AS Inserted;

GO
Open this example in the tool

Inventory sync with MERGE

Every WHEN branch of the MERGE starts a new line, and the update list uses leading commas.

Input
merge dbo.Inventory as t using staging.InventoryFeed as s on t.Sku = s.Sku when matched and t.Quantity <> s.Quantity then update set t.Quantity = s.Quantity, t.UpdatedAt = sysutcdatetime() when not matched by target then insert (Sku, Quantity, UpdatedAt) values (s.Sku, s.Quantity, sysutcdatetime()) when not matched by source then delete;
Output
MERGE
  dbo.Inventory AS t using staging.InventoryFeed AS s ON t.Sku = s.Sku
WHEN MATCHED
  AND t.Quantity <> s.Quantity THEN
UPDATE SET
  t.Quantity = s.Quantity
, t.UpdatedAt = sysutcdatetime()
WHEN NOT MATCHED BY TARGET THEN
INSERT
  (Sku, Quantity, UpdatedAt)
VALUES
  (s.Sku, s.Quantity, sysutcdatetime())
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
"[Order" is not valid Standard SQL syntaxThe Dialect was left on Standard SQL, which has no square-bracket identifiers.Choose T-SQL (SQL Server) in the Dialect menu and format again.
This quoted identifier is never closed — the [ has no matching ]A bracketed name lost its closing ], often when a column list was trimmed by hand.Close the name with ]. A literal ] inside a name must be written as ]].
This string is never closed — the ' has no matching '
Explained
An apostrophe inside a value was escaped C-style (N’can't ship’), which SQL Server does not support.Double the apostrophe: N’can’‘t ship’.
Unexpected "FROM" hereA CASE expression in the select list is missing its END, so FROM arrives while CASE is still open.Add END (and the alias, if any) after the last WHEN or ELSE branch.
This '(' is never closed
Explained
An IN list, ISNULL(…) call or derived table was not closed before the next clause.Add the missing ) at the end of the expression the error position points to.

Frequently asked questions

How do I format T-SQL from SQL Server Management Studio?

Copy the query from the SSMS editor, paste it here with T-SQL (SQL Server) selected, and press Ctrl/Cmd+Enter. Ctrl/Cmd+Shift+C copies the result back so you can paste it into SSMS.

Does the formatter keep GO separators?

Yes, GO is never removed. It stays on its own line before SELECT, INSERT, CREATE, SET and USE, but a DECLARE, PRINT or EXEC that opens a batch is joined to the GO line.

Can I format a stored procedure?

Yes. The statements inside the body are formatted fully, while the header, BEGIN TRY/CATCH and SET options get a looser layout that you may want to touch up.

Will brackets around reserved words be removed?

No. Bracketed identifiers are copied exactly, so [Order], [Key] and [Order Details] keep their brackets and their case.

Is it safe to paste production SQL?

The formatter runs entirely in your browser and makes no network call with the query, so connection strings or customer names in the script stay on your machine.

Related tools