Amazon Redshift SQL Formatter

Paste Redshift SQL, choose Amazon Redshift in the Dialect menu and press Ctrl+Enter. The formatter follows Redshift rules for quotes, `#temp` table names and load/unload commands.

Input

Settings

History

Load from URL

Redshift is PostgreSQL underneath, with its own additions

Redshift inherits most of its lexical rules from PostgreSQL 8, and PasteKit’s Amazon Redshift mode follows them:

  • A single quote inside a string is written twice: 'it''s'. A backslash is not treated as an escape, so 'it\'s' is reported as an unterminated string.
  • "table" or "Order Date" in double quotes is an identifier. That is how you query system views with reserved column names, as in svv_table_info.
  • #recent_orders is read as one name, so temporary tables written with the hash prefix are not split.
  • $1, $2 are the positional parameters used by prepared statements and the Data API. @ has no special meaning.

Choosing Standard SQL or MySQL instead would change these rules. MySQL mode, for example, reads #recent_orders as the start of a comment, so the rest of that line is never formatted.

DDL with distribution, sort keys and compression

CREATE TABLE statements are where Redshift differs most from other warehouses. The column list is indented one column per line, and arguments get normalised spacing: identity(1,1) comes out as IDENTITY (1, 1) and decimal(12,2) as DECIMAL(12, 2).

Two layout quirks are worth knowing. Each ENCODE clause is moved onto its own line under the column it belongs to, so customer_id INTEGER NOT NULL is followed by ENCODE AZ64 one line down. After the closing bracket, DISTSTYLE, DISTKEY and COMPOUND SORTKEY stay together on one line, and only some of those words are upper-cased (diststyle KEY distkey (...)). The DDL is still valid; tidy the casing by hand if it matters to you.

UNLOAD, COPY and maintenance commands

UNLOAD ('select ...') takes its query as a quoted string, so the inner SQL stays exactly as written, doubled quotes and all. The TO 's3://...' target, IAM_ROLE and FORMAT AS PARQUET follow on the closing line, and PARTITION BY starts a new one. If you want the unloaded query formatted too, paste it separately, format it, and put it back inside the quotes (doubling any single quotes).

COPY sales.orders FROM 's3://...' places FROM on its own line, followed by the credentials and options. ANALYZE and VACUUM format as one-liners, though VACUUM DELETE ONLY is split after VACUUM.

Words that Redshift treats as keywords are upper-cased even when you use them as column names. A column called region becomes REGION because it is also a COPY option. Redshift folds unquoted names to lower case, so this does not change what the query does; switch Keyword case to As written if the capitals bother you.

What will not format, and useful settings

Stored procedures written as CREATE PROCEDURE ... AS $$ ... $$ LANGUAGE plpgsql are rejected with "$$" is not valid Amazon Redshift syntax. Format the SQL statements from the procedure body on their own instead. JDBC-style ? placeholders are rejected too; use $1 or a sample value.

For long revenue and cohort queries, try Commas: Leading with Function case: UPPER, so aggregates like SUM, LISTAGG and DATEADD stand out from column names. Minify is handy before pasting SQL into a Lambda or Glue job string. For general conventions, see the SQL style guide.

Examples

Orders table with DISTKEY, SORTKEY and ENCODE

Columns go one per line, with each ENCODE clause on the line below its column.

Input
create table sales.orders (order_id bigint identity(1,1), customer_id integer not null encode az64, order_date date not null encode az64, status varchar(20) encode zstd, amount decimal(12,2) encode az64) diststyle key distkey (customer_id) compound sortkey (order_date, customer_id)
Output
CREATE TABLE sales.orders (
  order_id BIGINT IDENTITY (1, 1),
  customer_id INTEGER NOT NULL
  ENCODE AZ64,
  order_date date NOT NULL
  ENCODE AZ64,
  status VARCHAR(20)
  ENCODE ZSTD,
  amount DECIMAL(12, 2)
  ENCODE AZ64
) diststyle KEY distkey (customer_id) compound sortkey (order_date, customer_id)
Open this example in the tool

UNLOAD a year of orders to S3 as Parquet

The query inside UNLOAD is a string literal, so its doubled ‘’ quotes and lowercase text are left alone.

Input
unload ('select order_id, customer_id, amount, order_date from sales.orders where order_date >= ''2026-01-01''') to 's3://acme-exports/orders/' iam_role 'arn:aws:iam::123456789012:role/RedshiftUnload' format as parquet partition by (order_date)
Output
UNLOAD (
  'select order_id, customer_id, amount, order_date from sales.orders where order_date >= ''2026-01-01'''
) TO 's3://acme-exports/orders/' IAM_ROLE 'arn:aws:iam::123456789012:role/RedshiftUnload' FORMAT AS PARQUET
PARTITION BY
  (order_date)
Open this example in the tool

Monthly revenue per customer with running totals

Leading commas and upper-case function names make the nested window aggregate and LISTAGG easy to scan.

Input
select customer_id, date_trunc('month', order_date) as month, sum(amount) as revenue, sum(sum(amount)) over (partition by customer_id order by date_trunc('month', order_date) rows unbounded preceding) as lifetime_revenue, listagg(distinct status, ',') within group (order by status) as statuses, nvl(max(discount_code), 'none') as last_code from sales.orders where order_date >= dateadd(month, -12, getdate()) group by 1, 2
Output
SELECT
  customer_id
, DATE_TRUNC('month', order_date) AS month
, SUM(amount) AS revenue
, SUM(SUM(amount)) over (
    PARTITION BY
      customer_id
    ORDER BY
      DATE_TRUNC('month', order_date) rows unbounded preceding
  ) AS lifetime_revenue
, LISTAGG(DISTINCT status, ',') within GROUP (
    ORDER BY
      status
  ) AS statuses
, NVL(MAX(discount_code), 'none') AS last_code
FROM
  sales.orders
WHERE
  order_date >= DATEADD(month, -12, GETDATE())
GROUP BY
  1
, 2
Open this example in the tool

Temp table, COPY and ANALYZE in one script

The #recent_orders name stays whole, and each statement is separated by a blank line.

Input
create temp table #recent_orders as select order_id, customer_id, amount from sales.orders where order_date >= current_date - 7;
copy sales.orders from 's3://acme-raw/orders/2026/10/' iam_role 'arn:aws:iam::123456789012:role/RedshiftCopy' format as csv ignoreheader 1 region 'us-east-1';
analyze sales.orders;
Output
CREATE TEMP TABLE #recent_orders AS
SELECT
  order_id,
  customer_id,
  amount
FROM
  sales.orders
WHERE
  order_date >= current_date - 7;

COPY sales.orders
FROM
  's3://acme-raw/orders/2026/10/' IAM_ROLE 'arn:aws:iam::123456789012:role/RedshiftCopy' FORMAT AS CSV IGNOREHEADER 1 REGION 'us-east-1';

ANALYZE sales.orders;
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
This string is never closed — the ' has no matching '
Explained
A quote was escaped MySQL-style with a backslash, as in ‘it's’. Redshift mode does not treat backslash as an escape, so the literal never ends.Write the apostrophe as two single quotes: ‘it’‘s’. Inside an UNLOAD query string, every quote in the inner SQL needs doubling.
"$$" is not valid Amazon Redshift syntaxThe input is a stored procedure or function with a dollar-quoted body, which this dialect cannot parse.Format the statements from inside the body separately, then paste them back between the $$ markers.
"?" is not valid Amazon Redshift syntaxThe query contains ? placeholders from a JDBC or ODBC client.Use Redshift positional parameters ($1, $2) or temporarily substitute literal values.
"`orders`" is not valid Amazon Redshift syntaxThe query was copied from MySQL or BigQuery and still quotes names with backticks.Replace backticks with double quotes (“orders”), or drop the quotes if the name is a plain lower-case identifier.
This '(' is never closed
Explained
Often an UNLOAD statement whose closing ) after the quoted query was lost, or an unfinished IDENTITY(1, 1).Jump to the highlighted bracket and add the missing ) where the expression ends.

Frequently asked questions

Can it format the query inside UNLOAD?

Not in place, because that query is a string literal and strings are never changed. Format the inner SELECT on its own, then paste it back inside the quotes with every single quote doubled.

Does it support Redshift stored procedures?

No. Dollar-quoted procedure bodies are rejected in Redshift mode. You can still format each statement from the body individually.

Will it break #temp table names?

No. In Redshift mode a leading # is part of the identifier, so #recent_orders is kept whole. Picking MySQL mode by mistake would treat it as a comment.

Why did my region column turn upper case?

REGION is a Redshift keyword (it is a COPY option), so keyword casing applies to it. Unquoted names are case-insensitive in Redshift, so the query behaves the same.

Does my SQL get sent to AWS or a server?

No. Formatting happens entirely in your browser, which matters when queries contain IAM role ARNs or bucket names.

Related tools