SQL整形
長いSQLを句ごとに改行して読みやすく整形します。MySQL・PostgreSQL・SQLite の方言に対応。
使い方とメモ
- 整形は句ごとに改行してインデントを揃えます。圧縮は空白と改行を詰めて1行にします(コメントは落ちます)。
- 標準SQLで構文エラーになるときは、
方言を切り替えてみてください。MySQL の#コメント、PostgreSQL の::キャストや$1パラメータは、方言を選ばないと通りません。 - 構文エラーは「3行目 12列目」のように場所を指摘します。文字列の中や末尾の括弧の閉じ忘れを見つけるのに使えます。
- 変換はブラウザの中だけで動きます。本番のクエリをそのまま貼っても、どこにも送信されません。
方言で書き方が変わるところ
同じ SQL でも、データベースによって通る書き方が違います。このツールで「方言」を切り替えたときに結果が変わるのは、主に次の書き方です。
| 書き方 | 標準SQL | MySQL | PostgreSQL | SQLite |
|---|---|---|---|---|
`id` バッククォート | ○ | ○ | ✕ | ○ |
[id] 角括弧 | ✕ | ✕ | ○ | ○ |
# コメント | ✕ | ○ | ○ | ✕ |
'1'::int キャスト | ✕ | ✕ | ○ | ✕ |
$1 パラメータ | ✕ | ○ | ○ | ✕ |
:id パラメータ | ✕ | ✕ | ○ | ○ |
? パラメータ | ○ | ○ | ○ | ○ |
この表は「このツールで整形が通るか」で、実際のデータベースで同じ意味になるかとは別です。たとえば PostgreSQL の # はコメントではなくビットの排他的論理和(XOR)の演算子なので、整形は通っても意味が違います。迷ったら、普段使っているデータベースの方言を選んでください。
はまりやすい違いと落とし穴
文字列はシングルクォート、識別子はダブルクォート
標準SQLでは '…' が文字列、"…" が列名やテーブル名です。ところが MySQL は標準の設定では "…" も文字列として扱います(ANSI_QUOTES モードを有効にすると標準どおりになります)。MySQL で書いた WHERE name = "abc" を PostgreSQL に持っていくと、「abc という列がない」というエラーになります。
SQLite はさらに寛容で、ダブルクォートで書いた名前が列に見つからないと、エラーにせず文字列として扱うことがあります。列名を打ち間違えても、エラーにならずに列名の文字列そのものが返ってくるので気づきにくいところです。
-- SQLite で、存在しない列 "nmae" を指定した例 SELECT "nmae" FROM users LIMIT 1; -- → エラーではなく 'nmae' という文字列が返る
文字列の連結は || と CONCAT()
標準SQL・PostgreSQL・SQLite では 'a' || 'b' で文字列をつなげます。MySQL は標準の設定では || を OR として扱うため、同じ式が文字列ではなく 0 や 1 になります。MySQL では CONCAT('a', 'b') を使うのが確実です。
LIMIT の2つの数字の順番
MySQL と SQLite の LIMIT 10, 20 は「10件飛ばして20件」です。先に書くのが飛ばす件数で、取り出す件数ではありません。PostgreSQL はこの書き方を受け付けないので、どこでも通る LIMIT 20 OFFSET 10 と書いておくと読み違えません。
LIMIT 10, 20 -- MySQL・SQLite: 11件目から20件 LIMIT 20 OFFSET 10 -- 同じ意味。PostgreSQL でも通る
NULL は = で比べられない
NULL = NULL は真ではなく NULL(不明)になり、WHERE では偽と同じ扱いです。NULL を探すときは IS NULL・IS NOT NULL を使います。
✕ WHERE deleted_at = NULL ○ WHERE deleted_at IS NULL
同じ理由で、NOT IN のサブクエリの結果に NULL が1件でも混ざると、全体が1件も返らなくなります。サブクエリ側で WHERE … IS NOT NULL を付けるか、NOT EXISTS に書き換えてください。
-- user_id に NULL が混ざっていると 0 件になる SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders); -- NOT EXISTS なら NULL の影響を受けない SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
長いクエリを読むコツ
ログや ORM の出力から拾った1行のクエリは、整形してから次の順に読むと全体がつかみやすくなります。
- FROM と JOIN: どのテーブルをどうつないでいるか。
LEFT JOINの右側のテーブルにWHEREで条件を付けると、結果的にINNER JOINと同じになっていることがあります。 - WHERE: 整形すると
AND・ORが1行ずつに分かれます。ANDとORが混ざっているのに括弧がない条件は、意図どおりか確かめる価値があります(ANDが先に結びつきます)。 - GROUP BY と集計: SELECT に並ぶ列のうち、集計関数で囲まれていない列が GROUP BY にすべて入っているか。MySQL 8.4 の標準設定(
ONLY_FULL_GROUP_BY)では、主キーから値が決まる列を除き、入っていないとエラーになります。 - ORDER BY と LIMIT: 最後に何件・どの順で返すか。
圧縮はその逆で、整形したクエリをログや設定ファイルに1行で貼りたいときに使います。-- のコメントを残したまま1行にすると、それ以降がすべてコメントになって壊れるので、圧縮ではコメントを取り除いています。