結論

SQL の整形は、句(SELECT・FROM・WHERE など)ごとに改行し、列と条件を1行に1つ、かっこの中を字下げしてそろえる作業です。下のツールに貼るだけで整形でき、整形のあとは「実行結果が変わっていないか」を一度確かめるのが安全です。

この記事でわかること

  • SQL 整形ツールの使い方と、読みやすい SQL の書き方ルール
  • pg_format・sql-formatter・sqlfluff・sqlparse の出力と速さの実測
  • 整形・1行圧縮の前後で結果が変わらないことの確かめ方と実測

2026年11月7日更新|PostgreSQL 16.15・SQLite 3.53.1 で整形前後の結果を照合

SQL 整形を読み込み中です。

ツールの使い方:上の入力欄に SQL を貼ると、0.2秒ほどで下に整形した結果が出ます。選べるのは「キーワードを大文字/小文字/そのまま」「字下げ(スペース2つ・4つ・タブ)」「カンマを行の後ろ/前」の3つです。「1行に圧縮」を押すと改行を取り除いた1行の SQL になり、「整形した SQL をコピー」で結果を写せます。引用符(')やかっこの数が合わないときは注意が出ます。文法が正しいかどうかは確かめないので、整形した SQL は実行して結果を確かめます。入力した SQL はどこにも送信されません。

この記事で実際に動かしたもの:このページのツールと同じ整形処理、pg_format、sql-formatter、prettier-plugin-sql、sqlfluff、sqlparse、PostgreSQL、SQLite、Chromium。

flowchart TD
    A[読みにくい SQL] --> B[ツールで整形]
    B --> C{実行結果は<br>整形前と同じ?}
    C -->|同じ| D[コミット・共有]
    C -->|違う| E[圧縮や手直しの<br>差分を見直す]
    D --> F{チームで<br>そろえたい?}
    F -->|はい| G[sqlfluff の規則を<br>CI で確かめる]

よく調べられることの答えを先にまとめます。

やりたいこと いちばん早い方法 詳しい節
SQL を整形したい(すぐ) このページのツールに貼る。送信されない SQL整形ツールの使い方
SQL の書き方のルールを知りたい 句ごとに改行・1行に1項目・AND は行頭・かっこの中は字下げ 読みやすいSQLの書き方ルール
カンマは前と後ろどちらがよいか どちらでも結果は同じ。チームで1つに決める カンマの位置
コマンドで SQL を整形したい PostgreSQL なら pg_format、複数の方言なら sql-formatter SQLフォーマッターのコマンド比較
書き方のルールを自動で守らせたい sqlfluff の lint を CI で動かす sqlfluffでSQLの書き方をチェック
VS Code・DBeaver・A5:SQL Mk-2 で整形 DBeaver は Ctrl+Shift+F、A5:SQL Mk-2 は Ctrl+Q。VS Code は拡張機能が要る エディタでSQLを整形する
整形で結果が変わらないか確かめたい 整形前後を実行して EXCEPT ALL で差が0行か見る 整形前後で結果が変わらないことを確かめる
1行にまとめたら動かなくなった 行コメント(--)が残ったまま1行にした可能性が高い 整形・圧縮で結果が変わるケース

確認環境:Ubuntu 24.04.5 LTS の上で、このページのツールと同じ整形処理(サイトのテーマの JavaScript)を Node.js 22.23.4 で直接呼び出して出力を取り、同じ結果になることを Chromium 153.0.8010.12(Playwright 1.63.0)で開いたツールの画面でも確かめました。コマンドの整形は pg_format 5.5(Ubuntu の pgformatter パッケージを作業場所に展開)、sql-formatter 15.9.0、prettier 3.9.10+prettier-plugin-sql 0.20.0、sqlfluff 4.5.0、sqlparse 0.6.1(Python 3.13.18)で実行しています。整形前後の結果の照合は、127.0.0.1 だけで待ち受ける PostgreSQL 16.15 と、Python 標準の sqlite3 モジュール(SQLite 3.53.1)に同じ表と10件の注文データを入れて行いました。VS Code・DBeaver・A5:SQL Mk-2 の操作は手元で動かせないため、それぞれの公式ドキュメントで確かめた範囲だけを書いています。図は mermaid 11.16.0 で描画を確かめました(確認日 2026年11月7日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL整形ツール|見やすい書き方のルールと結果が変わらない確かめ方」

SQL整形とは(何のために整形するのか)

この節では、SQL 整形が何をする作業で、なぜ必要とされるのかが分かります。

SQL 整形(フォーマット)とは、SQL の意味を変えずに、改行・字下げ・大文字小文字だけをそろえて読みやすくすることです。データベースは空白や改行の量を気にしないので、1行に詰めた SQL も40行に広げた SQL も同じように動きます。読みやすさが変わるのは人間のほうです。

たとえば次の1行は、顧客ごとの支払い済み注文の件数と合計を出す SQL です。条件がどこからどこまでか、一目では追えません。

select c.id, c.name, count(o.id) as order_count, sum(o.amount) as total from customers c left join orders o on o.customer_id = c.id and o.status = 'paid' where c.created_at >= '2026-01-01' and c.deleted_at is null group by c.id, c.name having sum(o.amount) > 10000 order by total desc limit 10;

このページのツール(既定の設定:大文字・スペース2つ・カンマは後ろ)に貼ると、次のようになりました。

SELECT
  c.id,
  c.name,
  COUNT(o.id) AS order_count,
  SUM(o.amount) AS total
FROM
  customers c
LEFT JOIN orders o
  ON o.customer_id = c.id
  AND o.status = 'paid'
WHERE
  c.created_at >= '2026-01-01'
  AND c.deleted_at IS NULL
GROUP BY
  c.id,
  c.name
HAVING
  SUM(o.amount) > 10000
ORDER BY
  total DESC
LIMIT 10;

整形すると、次の3つがすぐ見えるようになります。

1何を取り出すか

SELECT の下に列が1行ずつ並ぶので、列の数(4つ)と別名がすぐ分かる。

2結合の条件

ON の下の AND が JOIN の条件だと分かる。WHERE の条件と混ざらない。

3差分の読みやすさ

1行に1項目なので、列を1つ足したときの Git の差分が1行で済む。

実務で SQL 整形が求められるのは、コードレビュー、障害調査でログに出た SQL を読むとき、ORM が組み立てた長い SQL を読むとき、チームで書き方をそろえたいとき、の4つが中心です。命名のそろえ方(テーブル名・列名)は整形とは別の話で、関連記事「DB命名規則一覧|テーブル名・カラム名・インデックス名・制約名」で扱っています。

SQL整形ツールの使い方とできること・できないこと

この節では、このページの SQL 整形ツールの操作と、整形の決まり、苦手な入力が分かります。

ツールは、SQL を「語」に区切り、句の始まりで改行し、かっこの深さに合わせて字下げします。文法を解析しているわけではないため、壊れた SQL でもそれらしく整形されます。動きは次のとおりです。

入力 ツールの整形
SELECT・FROM・WHERE・GROUP BY・HAVING・ORDER BY 句の前で改行し、続く列や条件を1段下げる
列の区切りのカンマ カンマごとに改行。「カンマは行の前」で行頭に置く
AND・OR 行頭に置いて1段下げる(BETWEEN の AND は改行しない)
JOIN・ON JOIN は句と同じ深さ、ON はその1段下
サブクエリ・WITH かっこの中を1段下げ、閉じかっこを開いた行の深さに戻す
CASE WHEN・ELSE を1段下げ、END を CASE の深さに戻す
関数・OVER のかっこ 改行せず1行のまま(COUNT(o.id) など)
セミコロン 文の終わりとして空行を1つ入れる

UPDATE・INSERT・ウィンドウ関数も、次のように整形されました。

UPDATE orders
SET
  status = 'cancel',
  amount = 0
WHERE
  id = 6
  AND status <> 'paid';

INSERT INTO orders (id, customer_id, amount, status)
VALUES
  (11, 2, 3000, 'paid');

SELECT
  customer_id,
  amount,
  row_number() OVER (PARTITION BY customer_id ORDER BY amount DESC) AS rn
FROM
  orders;

手元で Chromium のツール画面に入れて確かめた動きを、4つにまとめます。4つ目だけは、ツールの説明にあるとおり「文法までは見ない」ことから来る注意点です。

1方言のキーワードも大文字になる

PostgreSQL の ilike は「大文字」で ILIKE になった。ツールのキーワード一覧に無い語は、名前として入力のまま残る。

2:: は詰めたまま

PostgreSQL の型変換 id::text は、整形しても1行に圧縮しても id::text のまま残った。

3注意は本当の閉じ忘れだけ

コメントの「don't」や文字列の中のかっこでは注意が出ない。閉じていない引用符・合わないかっこだけで出る。

4末尾のカンマは見逃す

FROM の前に余分なカンマがあっても、そのまま整形される。エラーは実行して初めて分かる。

3つ目の例です。コメントに「don't」、文字列に「It''s (draft」を含む正しい SQL を貼っても、注意は出ませんでした。一方、引用符を閉じ忘れた SQL と、サブクエリの閉じかっこを忘れた SQL では、それぞれ次の注意が出ました。

引用符が閉じていない所があります。閉じ忘れがないか確かめます。
かっこの数が合っていません(「(」が 1 個多い)。

注意が出たら、その SQL はデータベースでもエラーになります(エラーの原文は「よくあるエラー」の節)。注意が出ないことは文法が正しいことの証明ではないので、最後は実行して確かめます。

読みやすいSQLの書き方ルール8つ

この節では、整形ツールが何をそろえているのか、手で書くときにも使える SQL の書き方ルールが分かります。

検索上位の記事やツールが共通して挙げるルールは、次の8つにまとまります。どれも「読む人が迷わない」ためのもので、データベースの動きには影響しません。

  1. 1句(SELECT・FROM・WHERE など)の前で改行する句が行頭に並ぶと、SQL の骨組みが縦に読める
  2. 2列・テーブル・条件は1行に1つ足す・消すときの差分が1行で済む
  3. 3AND・OR は行頭に置く条件の行をコメントにする・消すときに崩れにくい
  4. 4字下げの幅をそろえる(2か4のスペース)タブとスペースを混ぜない
  5. 5JOIN の ON は改行して1段下げる結合の条件と WHERE の条件を見分けられる
  6. 6キーワードは大文字、名前は小文字(どちらかに統一)混ざっていることが一番読みにくい
  7. 7テーブルの別名は意味の分かる短い名前にするa・b・c より、customers なら c、orders なら o
  8. 8サブクエリは WITH(CTE)に出して名前を付けるかっこの入れ子が浅くなり、上から順に読める

3番目の「AND は行頭」は、AND と OR が混ざった条件で特に効きます。AND と OR の優先順位やかっこの付け方は、関連記事「SQL WHEREの複数条件|AND・ORの優先順位とかっこ・NOTの書き方」で扱っています。

大文字か小文字か

キーワードを大文字にするのは、名前(テーブル・列)と見分けるためです。エディタの色分けがある今は小文字派も多く、sqlfluff の既定は「どちらかに統一されていればよい(consistent)」です。どちらを選んでも結果は変わらないことを、このページのツールで小文字・タブ字下げにした SQL を実行して確かめました(次の節の照合表)。決めたら混ぜないことが大事です。

別名に AS を付けるか

列の別名(count(o.id) AS order_count)には AS を付けるのが一般的です。テーブルの別名(customers c)は付けない書き方も多いですが、sqlfluff の既定の規則 AL01 は「テーブルの別名にも AS を付ける」を求め、sqlfluff fix は customers AS c に書き換えました。付けるかどうかもチームで決め、使うデータベースの文書で別名の書き方を確かめておきます。

カンマの位置(後ろか前か)とチームでの決め方

この節では、カンマを行の前に置く書き方(カンマ前置き)の良い点・気になる点と、どちらでも結果が同じことの実測が分かります。

カンマは後ろでも前でも、実行結果は同じです。ツールで「カンマは行の前」「スペース4つ」を選ぶと、次のようになりました。

SELECT
    c.id
    , c.name
    , COUNT(o.id) AS order_count
    , SUM(o.amount) AS total
FROM
    customers c
LEFT JOIN orders o
    ON o.customer_id = c.id
    AND o.status = 'paid'

カンマは後ろ(一般的)

文章と同じ並びで、初めて読む人にも自然

pg_format・sql-formatter・sqlfluff の既定

最後の列を消すと、1つ前の行の末尾カンマが残りやすい

カンマは前(カンマ前置き)

行頭がそろい、カンマの付け忘れが一目で分かる

最後の列を消しても他の行を直さずに済む

最初の列を消すと、次の行の先頭カンマが残る

カンマ前置きは「消すときに楽」とよく言われますが、手元で試すと、楽になるのは最後の列を消すときだけで、最初の列を消すときは逆に直す行が出ます。どちらかが正しいというより、チームで1つに決めて整形ツールの設定にしておくことが要点です。pg_format は -b(--comma-start)でカンマ前置きになり、sqlfluff は設定ファイルに [sqlfluff:layout:type:comma] と line_position = leading を書くと、fix でカンマが行頭に移りました。sql-formatter 15.9.0 は以前の版にあった commaPosition を受け付けず、渡すと ConfigError: commaPosition config is no more supported. で止まりました。

ありがちな失敗は、カンマを後ろに置いたまま最後の列を消して、FROM の前にカンマが残る形です。PostgreSQL と SQLite のエラーは次のとおりでした。

psql:e1.sql:5: ERROR:  syntax error at or near "FROM"
LINE 4: FROM
        ^

sqlite3.OperationalError: near "FROM": syntax error

サブクエリ・WITH句・CASE式の整形

この節では、入れ子のある SQL がどう整形されるか、ツールごとに違いが出る場所が分かります。

入れ子の SQL は、かっこの中を1段下げ、閉じかっこを開いた行の深さに戻すと、どこまでが1つのまとまりか分かります。WITH・CASE・SELECT の中のサブクエリ・IN の中のサブクエリを含む SQL を、このページのツールで整形した結果です。

WITH paid AS (
  SELECT
    customer_id,
    SUM(amount) AS total
  FROM
    orders
  WHERE
    status = 'paid'
  GROUP BY
    customer_id
)
SELECT
  c.name,
  CASE
    WHEN p.total >= 50000 THEN 'gold'
    WHEN p.total >= 10000 THEN 'silver'
    ELSE 'normal'
  END AS rank,
  (
    SELECT
      COUNT(*)
    FROM
      orders o2
    WHERE
      o2.customer_id = c.id
  ) AS cnt
FROM
  customers c
JOIN paid p
  ON p.customer_id = c.id
WHERE
  c.id IN (
    SELECT
      customer_id
    FROM
      orders
    WHERE
      amount BETWEEN 1000 AND 90000
  )
ORDER BY
  p.total DESC;

同じ SQL を他のツールに通すと、CASE と閉じかっこの位置に違いが出ました。

ツール CASE の形 サブクエリの閉じかっこ 行数
このページのツール WHEN ごとに1行 開いた行の深さで単独の行 41
sql-formatter 15.9.0 WHEN ごとに1行 単独の行(WITH の後に改行が入る) 41
pg_format 5.5 THEN の後で改行し、値を次の行に 最後の条件の行末に付ける 41
sqlfluff 4.5.0(fix) 短い WHEN を1行にまとめることがある 単独の行 29
sqlparse 0.6.1 WHEN ごとに1行(列の位置にそろえる) 最後の条件の行末に付ける 23

sqlfluff fix の CASE は「'gold' when」で行が切れる形になり、読みやすさではほかに劣りました。sqlfluff は「規則に反していないか」を直すツールで、見た目を一から組み直すツールではないためです。WITH 句の書き方そのものは、関連記事「SQL WITH句の使い方|CTEと再帰クエリをコピペSQLと実行結果で解説」で扱っています。

SQLフォーマッターのコマンド比較(pg_format・sql-formatter・sqlparse)

この節では、コマンドで SQL を整形する主なツールの使い方・出力の違い・速さが分かります。

PostgreSQL の SQL なら pg_format、複数の方言を扱うなら sql-formatter(Node.js)、Python の中で整形するなら sqlparse が手軽です。最初の1行の SQL を、それぞれ次のコマンドで整形しました。

# pg_format(Perl。-u 2 でキーワードを大文字、-s 2 で字下げ2)
pg_format -u 2 -s 2 s1.sql

# sql-formatter(npm。-l で方言を指定)
npx sql-formatter -l postgresql s1.sql

# sqlparse(Python)
python -c "import sqlparse;print(sqlparse.format(open('s1.sql').read(),reindent=True,keyword_case='upper'))"

pg_format の出力です。関数名(count・sum)は小文字のまま残り、JOIN は FROM の中に1段下げて置かれます。

FROM
  customers c
  LEFT JOIN orders o ON o.customer_id = c.id
    AND o.status = 'paid'
WHERE
  c.created_at >= '2026-01-01'
  AND c.deleted_at IS NULL

sqlparse の出力です。句のキーワードの後ろに続けて書き、2つ目以降の列を1つ目の列の位置にそろえます。

SELECT c.id,
       c.name,
       count(o.id) AS order_count,
       sum(o.amount) AS total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
AND o.status = 'paid'
WHERE c.created_at >= '2026-01-01'
  AND c.deleted_at IS NULL

sql-formatter は、設定を付けないとキーワードを入力のまま(この例では小文字)にし、LIMIT の後ろの 10 も次の行に下げました。{"keywordCase":"upper","tabWidth":2,"language":"postgresql"} を -c で渡すと大文字になります。prettier-plugin-sql(prettier 3.9.10)は既定で sql-formatter を中で使うため、出力は sql-formatter と同じでした。

速さの実測

WITH・CASE・サブクエリを含む SQL(455バイト)を50個と200個つなげたファイルで、整形にかかった時間を測りました。5つの作業が同時に動く CPU 2コアのマシンでの1回の値なので、目安として見ます。

0.08秒このページのツールの処理200文・91,000バイト
0.65秒sql-formatter同じ200文
49.7秒sqlfluff fix50文・22,750バイト
ツール 50文(22,750バイト) 200文(91,000バイト)
このページのツール(Node.js で直接実行) 0.06秒 0.08秒
sql-formatter 15.9.0 0.30秒 0.65秒
sqlparse 0.6.1 (測らず) 1.00秒
pg_format 5.5 0.61秒 2.35秒
sqlfluff 4.5.0(fix) 49.7秒(上限を外して実行) 既定では飛ばされた

sqlfluff は、20,000バイトを超えるファイルを既定で処理しません。200文のファイルでは次の警告が出て、何も直さずに終わりました。

WARNING    Length of file 'big2.sql' is 91000 bytes which is over the limit of 20000 bytes. Skipping to avoid parser lock. Users can increase this limit in their config by setting the 'large_file_skip_byte_limit' value, or disable by setting it to zero.
==== no fixable linting violations found ====

「直すものが無い」と表示されても、実際には調べていないので注意します。大きなマイグレーションのファイルを sqlfluff にかけるときは、設定ファイルに large_file_skip_byte_limit = 0 を書くか、ファイルを分けます。

sqlfluffでSQLの書き方をチェックする(lint・fix)

この節では、SQL のリンター(書き方の検査ツール)sqlfluff の使い方と、出力の読み方、設定ファイルの雛形が分かります。

整形ツールが「形をそろえる」のに対し、sqlfluff は「決めた規則に反している場所」を行番号つきで指摘し、直せるものは直します。チームの書き方を CI で守らせたいときに向いています。最初の1行の SQL を lint にかけた結果です。

pip install sqlfluff
sqlfluff lint lint1.sql --dialect postgres
== [lint1.sql] FAIL
L:   1 | P:   1 | LT05 | Line is too long (294 > 80). [layout.long_lines]
L:   1 | P:   1 | LT09 | Select targets should be on a new line unless there is
                       | only one select target. [layout.select_targets]
L:   1 | P:  88 | AL01 | Implicit/explicit aliasing of table. [aliasing.table]
L:   1 | P: 107 | AL01 | Implicit/explicit aliasing of table. [aliasing.table]
L:   1 | P: 112 | ST09 | Joins should list the table referenced earlier first.
                       | [structure.join_condition_order]
L:   1 | P: 155 | LT14 | The 'where' keyword should always start a new line.
                       | [layout.keyword_newline]
L:   1 | P: 215 | LT14 | The 'group' keyword should always start a new line.
                       | [layout.keyword_newline]

L は行、P は行の中の位置、その次が規則の番号です。よく出る規則を表にまとめます。

規則 意味 直し方
LT02 字下げの幅が違う(既定は4) tab_space_size を合わせるか、fix で直す
LT05 1行が長すぎる(既定は80文字) 改行するか max_line_length を変える
LT09 SELECT の列が2つ以上なのに改行していない 列を1行に1つにする
LT14 WHERE などの句が行頭にない 句の前で改行する
AL01 テーブルの別名に AS が無い customers AS c と書く
CP01 キーワードの大文字小文字が混ざっている どちらかにそろえる
CV03 SELECT の最後の列の後ろにカンマがある 余分なカンマを消す
ST09 JOIN の条件で、先に出たテーブルが左にない c.id = o.customer_id の順にする

このページのツールで整形した SQL も lint にかけると、LT02(Expected indent of 4 spaces)が並びました。ツールの字下げはスペース2つ、sqlfluff の既定は4つだからです。規則と整形ツールの設定は、最初にそろえておきます。

sqlfluff fix で直す

sqlfluff fix は、直せる違反をファイルに書き戻します。1行の SQL では「7 fixable linting violations found」と出て、次の形になりました(1文で0.75秒)。

select
    c.id,
    c.name,
    count(o.id) as order_count,
    sum(o.amount) as total
from customers as c
left join orders as o on c.id = o.customer_id and o.status = 'paid'
where c.created_at >= '2026-01-01' and c.deleted_at is null
group by c.id, c.name
having sum(o.amount) > 10000
order by total desc
limit 10;

AS の追加(AL01)と、結合条件の左右の入れ替え(ST09)は、字下げだけでなく SQL の字面も変えます。意味は同じですが、差分を見て驚かないよう知っておきます。字下げだけ直したいときは sqlfluff format を使うと、見た目の規則(LT 系)だけを直し、AS の追加や左右の入れ替えは行いませんでした。

設定ファイルの雛形

キーワード・関数・null を大文字、字下げ2、1行100文字にする設定です。プロジェクトの直下に .sqlfluff として置きました。

[sqlfluff]
dialect = postgres
max_line_length = 100

[sqlfluff:indentation]
tab_space_size = 2

[sqlfluff:rules:capitalisation.keywords]
capitalisation_policy = upper

[sqlfluff:rules:capitalisation.functions]
extended_capitalisation_policy = upper

[sqlfluff:rules:capitalisation.literals]
capitalisation_policy = upper

最後の3行(capitalisation.literals=規則 CP04)が無いと、キーワードは大文字になっても IS null の null だけ小文字で残りました。null・true・false は「キーワード」ではなく「リテラル」として別の規則で扱われるためです。この設定で fix したあと lint を流すと「All Finished!」だけが出て、終了コードは0でした。CI では、この終了コードで合否を判定します。設定ファイルは setup.cfg・tox.ini・pep8.ini・.sqlfluff・pyproject.toml の順に読まれ、後のファイルの値が前の値を上書きします(公式ドキュメント)。

エディタでSQLを整形する(VS Code・DBeaver・A5:SQL Mk-2)

この節では、よく使われるエディタ・データベースツールでの SQL 整形の操作が分かります。

DBeaver と A5:SQL Mk-2 は SQL 整形が最初から付いており、VS Code は拡張機能を入れると整形できます。いずれも手元では動かせないため、公式ドキュメントで確かめた操作だけを載せます。

ツール 整形の操作 設定できること(公式の記載)
VS Code Format Document:Windows は Shift+Alt+F、macOS は ⇧⌥F、Linux は Ctrl+Shift+I。選択範囲は Ctrl+K Ctrl+F 組み込みの整形は JavaScript・TypeScript・JSON・HTML・CSS だけ。SQL は Marketplace の Formatters の拡張機能で対応
DBeaver テキストを選んで Ctrl+Shift+F、または右クリックの Format → Format SQL To Upper Case・To Lower Case で大文字小文字を変える
A5:SQL Mk-2 メニューの[SQL]→[SQL の整形]、または Ctrl+Q 大文字小文字の変換、カンマや and/or の位置、字下げをオプション画面で指定

VS Code の保存時の自動整形(editor.formatOnSave)は既定でオフです。オンにする場合も、SQL の整形を受け持つ拡張機能が入っていなければ何も起きません。拡張機能を選ぶときは、使っているデータベースの方言(PostgreSQL・MySQL など)を指定できるかを見ます。次の節のとおり、方言を指定しないと整形に失敗する SQL があるためです。

手元にエディタが無い場面(他人のパソコン・ブラウザだけの環境)では、このページのツールが使えます。オンラインの整形サイトに貼るときは、送信されるかどうかをサイトの説明で確かめ、顧客名などが入った SQL を貼らないようにします。

方言(PostgreSQL・MySQL など)と整形の失敗

この節では、データベースごとの書き方の違い(方言)が整形にどう影響するかが分かります。

sql-formatter は、方言を指定しないと PostgreSQL の :: などを読めずに止まります。id::text を含む SQL を、方言を指定せずに渡した結果です。

Error: Parse error: Unexpected "::text as " at line 1 column 10.
This likely happens because you're using the default "sql" dialect.
If possible, please select a more specific dialect (like sqlite, postgresql, etc).

-l postgresql を付けると整形できました。sql-formatter 15.9.0 の --help に出る方言は bigquery・clickhouse・db2・db2i・duckdb・hive・mariadb・mysql・n1ql・plsql・postgresql・redshift・spark・sqlite・sql・tidb・trino・transactsql・tsql・singlestoredb・snowflake です。README には、ストアドプロシージャに対応しないこと、区切り文字を ; 以外に変える書き方に対応しないことも書かれています。

sqlfluff も --dialect か設定ファイルの dialect が必須で、方言ごとに文法を解析します。このページのツールは方言を見ずに語の並びだけで整形しますが、:: は前後を詰めたまま(id::text)残し、ilike も ILIKE に変えました。整形前・整形後・1行に圧縮した SQL を PostgreSQL で実行すると、結果は同じでした。なお、この SQL は SQLite では :: を使えないため「unrecognized token: ":"」で失敗します。整形ではなく SQL そのものの方言の違いです。

整形前後で結果が変わらないことを確かめる方法

この節では、整形した SQL が整形前と同じ結果を返すかを、データベースで確かめる手順と実測が分かります。

整形は空白と大文字小文字しか変えないはずですが、ツールの不具合や手直しの誤りで中身が変わることがあります。大事な SQL は、次の順で確かめます。

1

整形前と整形後を両方実行する

件数が同じかを見る。開発用のデータベースで、更新系の SQL は BEGIN と ROLLBACK で挟んで試す。

SELECT・UPDATE

2

EXCEPT ALL で差を取る

(整形前) EXCEPT ALL (整形後) と、その逆の両方が0行なら、行の中身も重複の数も同じ。

PostgreSQL・SQLite など

3

実行計画を比べる

EXPLAIN の出力を diff して同じなら、データベースは同じ SQL として解釈している。

PostgreSQL

4

文字列とコメントの差分を目で見る

git diff -w(空白の違いを無視)で、空白以外の差が文字列の中に無いかを見る。

Git

EXCEPT ALL と EXPLAIN で確かめたときの実物です。

$ psql -c "(整形前の SQL) EXCEPT ALL (整形後の SQL);"
 id | name | order_count | total
----+------+-------------+-------
(0 rows)

$ psql -At -c "EXPLAIN (COSTS OFF) 整形前" > ex1.txt
$ psql -At -c "EXPLAIN (COSTS OFF) 整形後" > ex2.txt
$ diff ex1.txt ex2.txt && echo "EXPLAIN 同じ"
EXPLAIN 同じ

3つの SQL を、このページのツールの設定違いと4つのコマンドで整形し、PostgreSQL と SQLite で実行して結果の md5(先頭8文字)を比べました。

SQL 整形の方法 PostgreSQL SQLite
集計(1行・3件) 整形前 3件 88e40b7a 3件 88e40b7a
集計 ツール(大文字・カンマ後ろ/前・小文字タブ) 3件 88e40b7a 3件 88e40b7a
集計 pg_format・sql-formatter・sqlfluff・sqlparse 3件 88e40b7a 3件 88e40b7a
WITH・CASE(6件) 整形前とツール3設定・コマンド4つ 6件 dd0a0868 6件 dd0a0868
文字列とコメントを含む(1件) 整形前とツール3設定・コマンド4つ 1件 9cb497c9 1件 9cb497c9
3つとも ツールの「1行に圧縮」 整形前と同じ md5 整形前と同じ md5
文字列を含む sed の正規表現で手作業の圧縮 1件 9f28b37d 1件 9f28b37d

整形(改行・字下げ)でも、ツールの「1行に圧縮」でも、結果はすべて一致しました。一致しなかったのは最後の行、sed の正規表現で空白を詰めて文字列の中身まで変わったケースだけです。理由は次の節で説明します。

整形・圧縮で結果が変わる・壊れるケース

この節では、整形や1行への圧縮で SQL が動かなくなったり、結果が変わったりする実例と避け方が分かります。

危ないのは、エディタの置換などで手作業で「改行や空白を取り除く」操作です。改行に意味がある行コメント(--)と、空白に意味がある文字列が、手元で問題になりました。このページのツールの「1行に圧縮」では、どちらも起きませんでした。

行コメントを残したまま1行にする

次の2行の SQL の改行を、エディタの置換で空白に変えて1行にしました。

select id, name -- 一覧
from customers where id = 1;

1行にすると「-- 一覧」より後ろがすべてコメントになり、FROM 以降が消えます。PostgreSQL と SQLite のエラーは次のとおりでした。FROM が消えたのに「列が無い」というエラーになるので、原因に気づきにくい形です。

psql:s4.bad.sql:1: ERROR:  column "id" does not exist
LINE 1: select id, name -- 一覧 from customers where id = 1;
               ^

sqlite3.OperationalError: no such column: id

このページのツールの「1行に圧縮」は、-- のコメントを取り除いてから1行にするため、SELECT id, name FROM customers WHERE id = 1; になり、正しく1件を返しました。/* */ のコメントは1行にしても問題が起きないので残り、SELECT id, /* 名前 */ name FROM customers WHERE id = 2; も整形前と同じ結果を返しました。

正規表現で空白を詰めると文字列も変わる

エディタや sed の置換で「かっこ・カンマの前後の空白を詰める」と、引用符の中の文字列にも置換がかかります。手元で sed -E 's/ *([(),;]) */\1/g' をかけると、WHERE note = 'It''s (draft' が 'It''s(draft' になり、別の行(id 3・高橋)が返りました。このページのツールの「1行に圧縮」は SQL を語に区切ってから組み立て直すため、文字列とコメントの中は変わらず、EXPLAIN の条件も整形前と同じでした。

整形前      :  Filter: (note = 'It''s (draft'::text)
ツールで1行に圧縮:  Filter: (note = 'It''s (draft'::text)
sed で空白を詰めた:  Filter: (note = 'It''s(draft'::text)

1行にまとめるときは、正規表現の置換ではなく、文字列とコメントを区別できる整形ツールを使います。どの方法でも、まとめたあとの SQL は前の節の方法で一度結果を確かめます。

1行にまとめたいとき

やってはいけないエディタで改行を空白に置換する。-- の後ろがすべてコメントになる
→
代わりにコメントを消してから1行にし、実行して件数と中身を確かめる

ログに出す・プログラムの文字列に埋め込むときに起きやすい

整形したらすぐ本番へ

やってはいけない見た目が整ったので、そのまま本番の更新 SQL として流す
→
代わりに開発用のデータベースで整形前後を実行し、git diff -w で空白以外の差を見る

UPDATE・DELETE は BEGIN と ROLLBACK で挟んで試す

LEFT JOIN では、相手のテーブルの条件を ON に書くか WHERE に書くかで結果が変わります。整形して ON の下と WHERE の下に条件が分かれて並ぶと、この違いに気づきやすくなります。JOIN の種類ごとの違いは、関連記事「SQLのJOINの違い|INNER JOINとLEFT JOINの使い分け」で扱っています。

SQL整形でよくあるエラー・つまずき

この節では、SQL 整形の前後でよく出るエラーの原文と、原因・直し方が分かります。

出た表示(原文) 出た場所 原因 直し方
syntax error at or near "FROM" PostgreSQL FROM の前に余分なカンマ 最後の列の後ろのカンマを消す
near "FROM": syntax error SQLite 同上 同上
unterminated quoted string at or near "'佐藤;" PostgreSQL 引用符の閉じ忘れ '佐藤' と閉じる。ツールでは引用符の注意が出た
unrecognized token: "'佐藤" SQLite 同上 同上
syntax error at or near ";" PostgreSQL サブクエリの閉じかっこ忘れ ) を足す。ツールでは「(」が1個多いと出た
incomplete input SQLite 同上 同上
column "id" does not exist PostgreSQL -- のコメントを残して1行にした コメントを消してから1行にする
Parse error: Unexpected "'佐藤; sql-formatter 引用符の閉じ忘れで整形できない SQL を直してから整形する
Parse error: Unexpected "::text as " sql-formatter 方言を指定していない -l postgresql を付ける
over the limit of 20000 bytes. Skipping sqlfluff 大きなファイルを既定で飛ばす large_file_skip_byte_limit を変える

末尾のカンマは、このページのツールも sql-formatter も指摘せず、そのまま整形して終了コード0で終わりました。見つけられたのは sqlfluff の CV03(Trailing comma in select statement forbidden)と、データベースでの実行だけです。整形できたことは、正しい SQL であることの証明になりません。

SQL整形ツールの選び方(判断表)

この節では、場面ごとにどの SQL 整形の方法を使えばよいかが分かります。

場面 向いている方法 理由
ログの SQL を今すぐ読みたい このページのツール 貼るだけ。送信されず、壊れた SQL でも形が見える
PostgreSQL のファイルをまとめて整形 pg_format apt で入り、-i で上書き、-b でカンマ前置き
MySQL・BigQuery など複数の方言 sql-formatter -l で方言を選べる。prettier からも使える
チームの書き方を CI で守らせる sqlfluff lint 規則ごとに指摘し、終了コードで合否が出る
Python のプログラムの中で整形 sqlparse pip で入り、関数1つで呼べる
DB ツールで書きながら整形 DBeaver・A5:SQL Mk-2 の整形機能 ショートカット1つ(Ctrl+Shift+F・Ctrl+Q)
1行にまとめてプログラムに埋め込む ツールの「1行に圧縮」+実行で確認 -- のコメントを取り除き、文字列の中は変えない

チームで導入するなら、次の順番がつまずきにくい流れです。

  • 1大文字小文字・字下げ幅・カンマの位置を決める迷ったら、大文字・スペース2か4・カンマは後ろ(各ツールの既定に近い)
  • 2整形ツールの設定ファイルをリポジトリに置く.sql-formatter.json か .sqlfluff。人によって設定が違う状態をなくす
  • 3整形だけのコミットを分ける中身の変更と混ぜると、レビューで差分が読めなくなる
  • 4CI で lint を動かす大きなファイルは large_file_skip_byte_limit に注意
  • 5整形前後の結果を一度照合するEXCEPT ALL が両方向0行なら同じ結果

UNION を使った長い SQL も、UNION ALL の前で改行すると2つの SELECT の境目が分かります。UNION と UNION ALL の違いは関連記事「SQL UNION ALLとUNIONの違い|重複の扱い・約5倍の速度差・エラー対処」で扱っています。

よくある質問

この節では、SQL 整形とフォーマッターについて、よく聞かれる質問への答えが分かります。

Q. SQL を整形すると、実行速度は変わりますか?
変わりません。データベースは空白と改行を読み飛ばします。手元の PostgreSQL では、整形前後で EXPLAIN の実行計画が完全に同じでした。

Q. このページのツールに貼った SQL は、どこかに送られますか?
送られません。整形はブラウザの中だけで行われます。それでも、パスワードや個人情報を含む SQL は、貼る前に値を置き換えておくのが安全です。

Q. ツールで整形できたら、SQL は正しいということですか?
いいえ。このページのツールは文法を確かめません。FROM の前の余分なカンマもそのまま整形されました。正しさはデータベースで実行して確かめます。

Q. キーワードは大文字と小文字、どちらが正解ですか?
どちらでも動きます。キーワードを大文字にすると名前と見分けやすく、上位の記事やツールの多くは大文字を勧めています。大事なのは1つのファイル・1つのチームの中で混ぜないことです。

Q. カンマを行の前に置くのはおかしいですか?
おかしくありません。結果は同じで、カンマの付け忘れが見つけやすい利点があります。ただし最初の列を消すときは直す行が出ます。チームで決めて、整形ツールの設定にしておきます。

Q. MySQL や Oracle の SQL も整形できますか?
このページのツールは方言を見ずに語の並びで整形するため、多くの SQL はそのまま整形できます。DECLARE・ILIKE・MERGE などはキーワードとして大文字・小文字の切り替えの対象ですが、改行するのは SELECT・FROM などの句だけなので、PL/SQL のような手続きの書き方は行の区切りが変わりません。コマンドなら sql-formatter の -l mysql・-l plsql を使います。

Q. 「1行に圧縮」はどんなときに使いますか?
SQL をプログラムの文字列やログ1行に収めたいときです。-- のコメントは取り除かれ、/* */ のコメントと文字列の中は変わりません。手元では、圧縮した3つの SQL が PostgreSQL・SQLite とも整形前と同じ結果を返しました。大事な SQL は、圧縮後も一度実行して確かめます。

Q. VS Code に SQL の整形機能はありますか?
組み込みの整形は JavaScript・TypeScript・JSON・HTML・CSS だけで、SQL は Marketplace の拡張機能を入れて使います(VS Code 公式ドキュメント)。入れたあとは Format Document(Windows は Shift+Alt+F)で整形できます。

Q. sqlfluff と整形ツールは、両方入れるべきですか?
個人で読むだけなら整形ツールだけで困りません。チームで書き方をそろえ、レビューの指摘を減らしたいなら sqlfluff の lint を CI に入れます。その場合は sqlfluff の fix か format を整形ツールとして使い、別の整形ツールと設定がぶつからないようにします。

Q. 長いサブクエリが読みにくいときは、整形するだけで読みやすくなりますか?
整形で字下げはそろいますが、入れ子が3段を超えると字下げだけでは追いにくくなります。サブクエリを WITH 句に出して名前を付けると、上から順に読める形になります。

まとめ

  • SQL 整形は、意味を変えずに改行・字下げ・大文字小文字をそろえる作業。このページのツールに貼れば、句ごとの改行とかっこの中の字下げが一度に済む。
  • 書き方のルールは8つ:句の前で改行、1行に1項目、AND は行頭、字下げをそろえる、ON は1段下げ、大文字小文字を統一、別名は意味のある名前、サブクエリは WITH へ。
  • コマンドは用途で選ぶ:PostgreSQL は pg_format、複数の方言は sql-formatter(方言の指定が必須)、規則の検査は sqlfluff(20,000バイト超は既定で飛ばす)。
  • 整形では結果は変わらなかった:5通りの方法で整形した SQL は、PostgreSQL・SQLite とも整形前と同じ結果。EXCEPT ALL と EXPLAIN の比較で確かめられる。
  • 危ないのは手作業で1行にまとめるとき:改行の置換は -- のコメントで後ろが消え、正規表現の置換は文字列の中も変える。ツールの「1行に圧縮」は結果が一致した。整形できても正しい SQL とは限らないので、最後は実行して確かめる。

参考・出典

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL整形ツール|見やすい書き方のルールと結果が変わらない確かめ方」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

成長のヒミツ、 ぜんぶ丸わかり