Spark SQL Formatter

Paste Spark SQL from a Databricks notebook, a Hive script or a `spark.sql()` call, select Spark SQL as the dialect and press Ctrl+Enter.

Input

Settings

History

Load from URL

Quoting rules that Spark shares with Hive

Spark SQL quotes names with backticks: web.sessions, or a path table like parquet.s3://acme-lake/orders/. Everything in backticks is one identifier, slashes and all.

String literals work differently from ANSI SQL:

  • Single and double quotes both create strings. "checkout" is text, not a column name. Code ported from PostgreSQL or Trino that double-quotes identifiers needs those quotes changed to backticks.
  • Backslash is the escape character, so an apostrophe is written 'it\'s'. A doubled '' is not an escape: 'it''s' is two adjacent literals, and the formatted output shows them apart as 'it' 's'.
  • Raw strings (r'^(\w+)-\d+$') keep backslashes literally, which is the easiest way to write regexes for regexp_extract and rlike. Hex literals like X'1F' are recognised too.

Comments are -- and /* */.

Hive-style clauses and Delta Lake DDL

The dialect knows the clauses that Hive-era Spark code is full of. LATERAL VIEW EXPLODE(...) gets its own line between FROM and WHERE. CLUSTER BY, DISTRIBUTE BY and SORT BY are laid out as clauses, the same way as GROUP BY. INSERT OVERWRITE TABLE ... PARTITION (dt = '2026-03-01') keeps the partition spec next to the table name, and the SELECT feeding it starts a new block.

For CREATE TABLE ... USING delta PARTITIONED BY (...) TBLPROPERTIES (...), the column list is indented one column per line and the table options follow the closing bracket. Delta Lake time travel (VERSION AS OF 12, TIMESTAMP AS OF '2026-03-01'), CACHE TABLE and OPTIMIZE ... ZORDER BY all format without errors.

Lambdas in higher-order functions (aggregate(items, 0, (acc, x) -> acc + x.qty)) are left on one line with the arrow intact.

Known gaps in Spark support

A few patterns from notebooks and jobs do not format cleanly, so check for them:

  • Parameter markers. Both ? and named :region markers are rejected as invalid Spark SQL syntax. Put a literal in while formatting.
  • Variable substitution glued to a name. ${db}.orders and '${run_date}' are fine, but ${env}_orders becomes ${env} _orders, which changes the table name. Wrap the whole name in backticks or substitute the variable first.
  • TRANSFORM and FILTER as functions. These collide with the Hive TRANSFORM clause and the FILTER keyword, so transform(items, x -> ...) is pushed to the left margin like a clause. The SQL is unchanged, just oddly indented. Other higher-order functions, such as aggregate, keep normal indentation.
  • Multi-column LATERAL VIEW aliases (t AS pos, item) are broken after the comma.
  • Delta MERGE with UPDATE SET * stays mostly on one long line.
  • Databricks colon paths for JSON strings (payload:customer.id) are not part of this grammar; use get_json_object or from_json.

Useful settings for notebook SQL

Notebook cells are read in narrow panes, so a smaller line width plus Indent style: Standard usually reads best. Keyword case UPPER is the common Databricks convention; table options like delta in USING delta keep their original case.

When you embed SQL in PySpark (spark.sql("...")), minify it first or keep it formatted in a triple-quoted Python string. Minify removes comments and whitespace but never edits text inside quotes or backticks. Read the SQL style guide for naming and layout conventions that suit lakehouse code.

Examples

Explode session events into rows

LATERAL VIEW sits on its own line, and the double-quoted event names are kept as string literals.

Input
select s.session_id, s.user_id, e.event_name, e.event_ts from `web`.`sessions` s lateral view explode(s.events) x as e where s.dt = '2026-03-01' and e.event_name in ("add_to_cart", "checkout") -- cart funnel
Output
SELECT
  s.session_id,
  s.user_id,
  e.event_name,
  e.event_ts
FROM
  `web`.`sessions` s
LATERAL VIEW EXPLODE (s.events) x AS e
WHERE
  s.dt = '2026-03-01'
  AND e.event_name IN ("add_to_cart", "checkout") -- cart funnel
Open this example in the tool

Delta table DDL and a partition overwrite

Column definitions are listed one per line, and the PARTITION spec stays beside the target table.

Input
create table if not exists sales.orders (order_id bigint, customer_id bigint, amount decimal(12,2), order_date date) using delta partitioned by (order_date) tblproperties ('delta.autoOptimize.optimizeWrite' = 'true');
insert overwrite table sales.daily_revenue partition (dt = '2026-03-01') select customer_id, sum(amount) as revenue from sales.orders where order_date = '2026-03-01' group by customer_id
Output
CREATE TABLE IF NOT EXISTS sales.orders (
  order_id BIGINT,
  customer_id BIGINT,
  amount DECIMAL(12, 2),
  order_date DATE
) USING delta PARTITIONED BY (order_date) TBLPROPERTIES ('delta.autoOptimize.optimizeWrite' = 'true');

INSERT OVERWRITE TABLE
  sales.daily_revenue PARTITION (dt = '2026-03-01')
SELECT
  customer_id,
  sum(amount) AS revenue
FROM
  sales.orders
WHERE
  order_date = '2026-03-01'
GROUP BY
  customer_id
Open this example in the tool

Session durations with DISTRIBUTE BY and SORT BY

DISTRIBUTE BY and SORT BY each start a clause, and commas lead every column line.

Input
with s as (select session_id, min(event_ts) as started, max(event_ts) as ended, count(*) as events from web.events where dt = '2026-03-01' group by session_id) select session_id, unix_timestamp(ended) - unix_timestamp(started) as duration_s, events, dense_rank() over (order by events desc) as rnk from s distribute by session_id sort by rnk
Output
WITH
  s AS (
    SELECT
      session_id
    , min(event_ts) AS started
    , max(event_ts) AS ended
    , count(*) AS events
    FROM
      web.events
    WHERE
      dt = '2026-03-01'
    GROUP BY
      session_id
  )
SELECT
  session_id
, unix_timestamp(ended) - unix_timestamp(started) AS duration_s
, events
, dense_rank() OVER (
    ORDER BY
      events DESC
  ) AS rnk
FROM
  s
DISTRIBUTE BY
  session_id
SORT BY
  rnk
Open this example in the tool

Query Parquet files directly with a raw-string regex

The backticked S3 path is one identifier, and the r’’ regex keeps its backslashes.

Input
select order_id, from_json(payload, 'struct<sku:string,qty:int>') as item, regexp_extract(order_ref, r'^(\w+)-\d+$', 1) as channel from parquet.`s3://acme-lake/raw/orders/dt=2026-03-01/` where get_json_object(payload, '$.status') = "paid"
Output
SELECT
  order_id,
  from_json(payload, 'struct<sku:string,qty:int>') AS item,
  regexp_extract(order_ref, r'^(\w+)-\d+$', 1) AS channel
FROM
  parquet.`s3://acme-lake/raw/orders/dt=2026-03-01/`
WHERE
  get_json_object(payload, '$.status') = "paid"
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 literal ends in a backslash, such as 'C:\data' or 'a', and Spark reads ' as an escaped quote.Double the final backslash (‘C:\data\’) or use a raw string (r’C:\data').
":region" is not valid Spark SQL syntaxA named parameter marker from spark.sql(query, args) was left in the text.Replace :region with a literal such as ‘EU’ while formatting, then restore the marker.
"?" is not valid Spark SQL syntaxThe query uses positional ? markers from a JDBC client or a parameterised spark.sql call.Substitute sample values for the markers before formatting.
":customer." is not valid Spark SQL syntaxDatabricks JSON path syntax (payload:customer.id) is not supported by the Spark SQL grammar used here.Rewrite the path as get_json_object(payload, ‘$.customer.id’) or parse the column with from_json.
This '(' is never closed
Explained
Typically an explode(…), from_json(…) or nested struct call missing its last bracket.Follow the highlight to the opening bracket and close it where the argument list ends.

Frequently asked questions

Does this work for Databricks SQL?

Yes for the Spark SQL core: backticks, Delta DDL, time travel, LATERAL VIEW, MERGE and window functions. Databricks-only additions such as colon JSON paths and :named parameters are not supported.

Can I format Hive QL with it?

Most HiveQL formats correctly in Spark SQL mode, because the quoting rules and clauses such as DISTRIBUTE BY, SORT BY and LATERAL VIEW are shared.

Why did my double-quoted column name become a string?

It did not change, but with default settings Spark reads “name” as a string literal. Use backticks for identifiers that need quoting.

Will it mangle ${variable} substitutions?

A ${var} that stands alone or sits inside quotes is preserved. When it is glued to more letters, as in ${env}_orders, a space is inserted, so substitute or backtick-quote such names first.

Is the SQL I paste uploaded anywhere?

No. The formatter runs in the browser, so notebook code, bucket paths and table names stay on your machine.

Related tools