Oracle SQL and PL/SQL Formatter

Choose PL/SQL (Oracle) as the dialect, paste a query or an anonymous block, and format it with Ctrl/Cmd+Enter. Oracle-only quoting and variables are recognised before any layout happens.

Input

Settings

History

Load from URL

Oracle literals and variables

Oracle code mixes server SQL with SQL*Plus conventions, and the PL/SQL dialect reads both:

  • Alternative quoting: q'[Customer's note]', q'{…}', q'<…>' and q'#…#' hold apostrophes without doubling them. nq'…' works too. In any other dialect the first inner apostrophe ends the string, so the rest of the query falls apart.
  • Ordinary strings double their quotes ('O''Brien'), and N'…' marks national-character text.
  • Double quotes are case-sensitive identifiers such as "OrderTotal". Backticks are rejected.
  • Bind variables like :cust_id and :new_id stay attached to their colon.
  • Substitution variables from SQL*Plus and SQL Developer scripts, &region and &&status, are kept as single tokens.
  • Hints: /*+ index(o orders_cust_ix) */ and line hints starting with --+ are recognised as hints rather than ordinary comments.

Hierarchical queries, MERGE and other Oracle syntax

START WITH and CONNECT BY PRIOR each get a line of their own, and ORDER SIBLINGS BY is treated as an ordering clause, so an org-chart or bill-of-materials query reads top to bottom. LEVEL, NOCYCLE and SYS_CONNECT_BY_PATH keep their place. MERGE INTO … USING (subquery) ON (…) indents the source query and puts each WHEN MATCHED THEN UPDATE SET / WHEN NOT MATCHED THEN INSERT branch on new lines.

The legacy outer-join marker is kept, with a space added before it: o.customer_id (+). FETCH FIRST 10 ROWS ONLY, RETURNING order_id INTO :new_id, ROWNUM filters and functions like DECODE and NVL all format as you would expect. Two oddities to know: EXTRACT(YEAR FROM order_date) is spread over several lines, and a column called name is upper-cased because NAME is a keyword.

PL/SQL blocks, procedures and the slash

This is a SQL formatter that tolerates PL/SQL rather than a full PL/SQL pretty-printer, and it helps to know where the line falls. In an anonymous block, every SQL statement between BEGIN and END is laid out properly, but the statements are not indented under BEGIN, DECLARE v_total NUMBER; stays on one line, and EXCEPTION WHEN no_data_found THEN … is joined into a single line. The / that ends a block in SQL*Plus is kept on its own line.

For CREATE OR REPLACE PROCEDURE, PACKAGE BODY and TRIGGER, the header is split after CREATE, because OR is read as a logical operator. CREATE OR REPLACE VIEW is not affected. For a procedure or package, joining the first two lines back together is usually the only edit needed. Trigger headers come out more broken up, with BEFORE, INSERT and ON orders on separate lines.

Choosing options for Oracle code

Many Oracle shops write upper-case keywords and lower-case names, which the defaults produce. Tabular indent styles misplace START WITH and CONNECT BY, splitting them as START WITH, so prefer Standard for hierarchical queries. When you need a one-line query for a log or a JDBC string, Minify (Ctrl/Cmd+Shift+M) removes ordinary comments but keeps /*+ */ and --+ hints, which the optimizer still needs. Everything stays in the browser, so schema names from a production instance are not sent anywhere.

Examples

Employee hierarchy with CONNECT BY

START WITH, CONNECT BY PRIOR and ORDER SIBLINGS BY each start a new line in a classic hierarchy query.

Input
select employee_id, last_name, manager_id, level as depth, sys_connect_by_path(last_name, '/') as chain from employees start with manager_id is null connect by prior employee_id = manager_id order siblings by last_name;
Output
SELECT
  employee_id,
  last_name,
  manager_id,
  LEVEL AS depth,
  sys_connect_by_path(last_name, '/') AS chain
FROM
  employees
START WITH manager_id IS NULL
CONNECT BY PRIOR employee_id = manager_id
ORDER SIBLINGS BY
  last_name;
Open this example in the tool

Daily stock MERGE

The USING subquery is indented and the matched and not-matched branches are separated.

Input
merge into inventory t using (select sku, sum(qty) as qty from stock_moves where moved_at >= trunc(sysdate) group by sku) s on (t.sku = s.sku) when matched then update set t.qty = t.qty + s.qty, t.updated_at = systimestamp when not matched then insert (sku, qty, updated_at) values (s.sku, s.qty, systimestamp);
Output
MERGE INTO
  inventory t USING (
    SELECT
      sku,
      sum(qty) AS qty
    FROM
      stock_moves
    WHERE
      moved_at >= trunc(sysdate)
    GROUP BY
      sku
  ) s ON (t.sku = s.sku)
WHEN MATCHED THEN
UPDATE SET
  t.qty = t.qty + s.qty,
  t.updated_at = systimestamp
WHEN NOT MATCHED THEN
INSERT
  (sku, qty, updated_at)
VALUES
  (s.sku, s.qty, systimestamp);
Open this example in the tool

Top orders with q-quoting and binds

A q’[…]’ literal with an apostrophe inside, a bind variable, a substitution variable and FETCH FIRST.

Input
select o.order_id, o.order_total, q'[Customer's "priority" note]' as label from orders o where o.customer_id = :cust_id and o.region = '&region' and o.order_date >= date '2026-01-01' order by o.order_total desc fetch first 10 rows only;
Output
SELECT
  o.order_id,
  o.order_total,
  q'[Customer's "priority" note]' AS label
FROM
  orders o
WHERE
  o.customer_id = :cust_id
  AND o.region = '&region'
  AND o.order_date >= DATE '2026-01-01'
ORDER BY
  o.order_total DESC
FETCH FIRST
  10 rows ONLY;
Open this example in the tool

Anonymous block ending with a slash

Shows what happens to a PL/SQL block: the SELECT is laid out, the rest keeps a flat layout and the / survives.

Input
declare
  v_total number;
begin
  select sum(order_total) into v_total from orders where customer_id = 101;
  dbms_output.put_line('Total: ' || v_total);
exception
  when no_data_found then
    v_total := 0;
end;
/
Output
DECLARE v_total NUMBER;

BEGIN
SELECT
  sum(order_total) INTO v_total
FROM
  orders
WHERE
  customer_id = 101;

dbms_output.put_line ('Total: ' || v_total);

EXCEPTION WHEN no_data_found THEN v_total := 0;

END;

/
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
This q'[…]' string is never closedAn alternative-quoted literal must end with the matching closer followed by a quote (]'), not with a bare apostrophe.End the literal with ]’ (or }‘, >’, or the same character you opened with).
This string is never closed — the ' has no matching '
Explained
The query uses q’[…]’ quoting but the Dialect is not PL/SQL (Oracle), so the apostrophe inside ends the string early.Switch the Dialect to PL/SQL (Oracle); in other databases, double the inner apostrophe instead.
"::int" is not valid PL/SQL (Oracle) syntaxA PostgreSQL-style cast was copied into Oracle SQL.Use CAST(total AS NUMBER(12,2)) or TO_NUMBER(total) instead.
"`id`" is not valid PL/SQL (Oracle) syntaxBacktick quoting from MySQL is not part of Oracle SQL.Quote the name with double quotes (“id”), remembering that Oracle then treats it as case-sensitive.
This '(' is never closed
Explained
A DECODE(…) or NVL(…) call, or a USING subquery in a MERGE, is missing its closing parenthesis.Add the ) where the expression ends; the error marks the bracket that was opened.

Frequently asked questions

How is this different from the formatter in SQL Developer?

SQL Developer formats with Ctrl+F7 inside the IDE using its own rules. This page needs no install, and it adds leading commas and a hint-preserving minifier, which helps when you only have a browser.

Does it support PL/SQL packages and procedures?

It accepts them and formats every SQL statement inside, but block structure is not indented. Use it to make a long procedure readable, not as a substitute for a dedicated PL/SQL style tool.

Are SQL*Plus substitution variables like &region supported?

Yes. A bare &order_id or &&order_id is kept as one token, and ‘&region’ inside a string is left untouched, so the script still prompts for them when run.

Will minify remove my optimizer hints?

No. /*+ … */ block hints and --+ line hints are kept, and a line hint keeps its line break so it does not comment out the rest of the query.

Can I use it for Oracle SQL without PL/SQL?

Yes. The PL/SQL (Oracle) dialect covers plain Oracle queries, DDL and DML as well as procedural blocks.

Related tools