VSCodeで「SQL整形ツール」を実現するTypeScriptマクロ【sql-formatter活用で複数方言対応】

Development
スポンサーリンク
スポンサーリンク

はじめに:読みづらいSQLに苦労していませんか?

開発やデータ分析の現場で、こんな経験はありませんか?

SELECT u.id,u.name,u.email,o.order_id,o.total FROM users u INNER JOIN orders o ON u.id=o.user_id WHERE u.status='active' AND o.created_at>='2026-01-01' ORDER BY o.created_at DESC;

改行もインデントもない1行のSQLクエリ。ログから抽出したSQL、他人が書いた圧縮されたクエリ、古いシステムから取り出したクエリ…。

こういったSQLを読み解く時、手作業で整形するのは本当に面倒です:

  1. 手動でキーワードごとに改行
  2. インデントを調整
  3. カンマの位置を揃える
  4. 大文字・小文字を統一

エディタで1つずつ編集するのは時間の無駄です。

もし、VSCodeで選択したSQLを1キー操作で整形し、新規ファイルに出力できたら便利だと思いませんか?

さらに、複数のSQL方言(MySQL、PostgreSQL、Oracle等)に対応し、キーワードの大文字/小文字やインデント幅を選択できたら?

今回、sql-formatterライブラリを活用したSQL整形ツールをVSCodeのTypeScriptマクロとして実装しました。

同じようにVSCodeで少しでも効率化したい開発者の方に、ぜひ参考にしていただきたい内容です。

Amazon検索[本 TypeScript]

この記事で分かること

  • SQL整形ツールの機能と使い方
  • sql-formatterライブラリの活用方法
  • 対応するSQL方言(8種類)
  • オプション選択(キーワードケース、インデント幅)
  • TypeScript実装の詳細コード
  • エラーハンドリングとユーザビリティ
  • 実務での活用例

なぜSQL整形ツールが必要なのか?

開発現場でのSQL整形の課題

課題1:ログから抽出したSQLが読めない

SELECT*FROM users WHERE status='active'AND created_at>='2026-01-01'ORDER BY id DESC LIMIT 10;

→ 本番環境のログから取り出したクエリは、改行もインデントもなく圧縮されている

課題2:SQL方言の違いに対応できない

  • MySQL独自の構文(LIMIT、バッククォート)
  • PostgreSQL独自の構文(::型キャスト、RETURNING)
  • Oracle独自の構文(DUAL、ROWNUM、PL/SQL)
  • SQL Server独自の構文(TOP、[]識別子)

→ 汎用的な整形ツールでは方言の違いを正しく扱えない

課題3:コーディング規約の統一

  • プロジェクトA:キーワードは大文字(SELECT、FROM)
  • プロジェクトB:キーワードは小文字(select、from)
  • プロジェクトC:インデントは2スペース
  • プロジェクトD:インデントは4スペース

→ 手作業での統一は非効率

既存のSQL整形ツールの問題点

オンライン整形ツール:

  • ブラウザで別サイトを開く手間
  • セキュリティの懸念(機密SQLを外部に送信)
  • ネットワーク接続が必要

DBクライアントツール:

  • 特定のDBにしか対応していない
  • エディタ上で完結しない
  • ツールの立ち上げに時間がかかる

VSCode拡張機能:

  • 設定が複雑
  • カスタマイズ性が低い
  • 出力先が固定

このマクロで実現できること

項目オンラインツールDBクライアントこのマクロ
方言対応⚠️ 限定的⚠️ 特定DBのみ✅ 8種類対応
セキュリティ❌ 外部送信⚠️ ツール依存✅ ローカル完結
操作性❌ ブラウザ切替❌ ツール切替✅ 1キー操作
カスタマイズ❌ 不可⚠️ 限定的✅ 柔軟に設定
出力先❌ 手動コピペ⚠️ 同一ファイル✅ 新規ファイル

実装した機能の概要

できること

選択されたSQLを整形して新規ファイルに出力

ステップ1:整形したいSQLを選択(または何も選択せずファイル全体)
ステップ2:コマンド実行
ステップ3:オプション選択ダイアログ
  - SQL方言(MySQL、PostgreSQL、Oracle等)
  - キーワードの大文字/小文字
  - インデント幅(2スペース、4スペース、タブ)
ステップ4:整形されたSQLが右側に新規ファイルで表示
ステップ5:保存ダイアログでファイル名を指定して保存

対応SQL方言(8種類)

1. Standard SQL(標準SQL)

-- デフォルト、最も汎用的
SELECT * FROM users WHERE id = 1;

2. MySQL / MariaDB

-- バッククォート、LIMIT構文
SELECT `id`, `name` FROM `users` LIMIT 10;

3. PostgreSQL

-- 型キャスト、RETURNING構文
SELECT id::INTEGER, name FROM users RETURNING *;

4. T-SQL (Microsoft SQL Server)

-- TOP構文、[]識別子
SELECT TOP 10 [id], [name] FROM [users];

5. PL/SQL (Oracle Database)

-- DUAL、ROWNUM、PL/SQLブロック
SELECT * FROM dual;
SELECT * FROM users WHERE ROWNUM <= 10;

6. Amazon Redshift

-- Redshift固有の構文
SELECT * FROM users LIMIT 10;

7. IBM DB2

-- DB2固有の構文
SELECT * FROM users FETCH FIRST 10 ROWS ONLY;

8. MariaDB

-- MariaDB固有の構文(MySQLベース)
SELECT * FROM users LIMIT 10;

オプション選択機能

1. SQL方言の選択

┌─────────────────────────────────────┐
│ SQL方言を選択してください           │
├─────────────────────────────────────┤
│ ○ Standard SQL (標準SQL)            │
│ ○ MySQL / MariaDB                   │
│ ○ PostgreSQL                        │
│ ○ T-SQL (SQL Server)                │
│ ○ PL/SQL (Oracle)                   │
│ ○ Amazon Redshift                   │
│ ○ IBM DB2                           │
└─────────────────────────────────────┘

2. キーワードの大文字/小文字

┌─────────────────────────────────────┐
│ キーワードの大文字/小文字を選択     │
├─────────────────────────────────────┤
│ ○ 大文字 (SELECT, FROM, WHERE)      │
│ ○ 小文字 (select, from, where)      │
│ ○ 元のまま保持                      │
└─────────────────────────────────────┘

3. インデント幅

┌─────────────────────────────────────┐
│ インデント幅を選択                  │
├─────────────────────────────────────┤
│ ○ 2スペース (推奨)                  │
│ ○ 4スペース                         │
│ ○ タブ                              │
└─────────────────────────────────────┘

整形例

整形前:

SELECT u.id,u.name,u.email,o.order_id,o.total,o.created_at FROM users u INNER JOIN orders o ON u.id=o.user_id WHERE u.status='active' AND o.total>=1000 AND o.created_at>='2026-01-01' ORDER BY o.created_at DESC,o.total DESC LIMIT 100;

整形後(大文字・2スペース):

SELECT
  u.id,
  u.name,
  u.email,
  o.order_id,
  o.total,
  o.created_at
FROM
  users u
  INNER JOIN orders o ON u.id = o.user_id
WHERE
  u.status = 'active'
  AND o.total >= 1000
  AND o.created_at >= '2026-01-01'
ORDER BY
  o.created_at DESC,
  o.total DESC
LIMIT
  100;

整形後(小文字・4スペース):

select
    u.id,
    u.name,
    u.email,
    o.order_id,
    o.total,
    o.created_at
from
    users u
    inner join orders o on u.id = o.user_id
where
    u.status = 'active'
    and o.total >= 1000
    and o.created_at >= '2026-01-01'
order by
    o.created_at desc,
    o.total desc
limit
    100;

具体的な使用場面とメリット

場面1:本番環境のログからSQLを抽出して分析

シーン:パフォーマンス問題の調査

application.log
---
[2026-02-15 10:30:45] Slow Query: SELECT u.id,u.name,p.product_id,p.name,p.price FROM users u JOIN purchases pu ON u.id=pu.user_id JOIN products p ON pu.product_id=p.id WHERE u.created_at>='2026-01-01' ORDER BY pu.created_at DESC;

手順:

  1. SQLの部分を選択
  2. コマンド実行
  3. 方言選択(MySQL)
  4. 大文字・2スペース
  5. 整形されたSQLを確認してインデックス追加の検討

メリット:

  • 圧縮されたログからSQLを即座に読みやすく変換
  • 結合条件やWHERE句を視覚的に確認
  • パフォーマンスチューニングの第一歩

場面2:他人が書いた複雑なクエリをレビュー

シーン:コードレビュー

-- レビュー対象のSQL(圧縮されている)
select u.id,u.name,u.email,count(o.order_id) as order_count,sum(o.total) as total_sales from users u left join orders o on u.id=o.user_id where u.status='active' group by u.id,u.name,u.email having count(o.order_id)>0 order by total_sales desc;

手順:

  1. SQLを選択
  2. コマンド実行
  3. 方言選択(Standard SQL)
  4. 大文字・2スペース
  5. 整形されたSQLでロジックを確認

メリット:

  • GROUP BYとHAVINGの条件を明確化
  • 結合条件の妥当性を視覚的に確認
  • レビューコメントを的確に指摘

場面3:マイグレーションスクリプトの整形

シーン:データベース移行作業

-- 古いシステムから取り出したDDL(フォーマットが崩れている)
create table users(id int primary key,name varchar(100) not null,email varchar(255) unique not null,status varchar(20) default 'active',created_at timestamp default current_timestamp);

手順:

  1. DDLを選択
  2. コマンド実行
  3. 方言選択(PostgreSQL)
  4. 大文字・2スペース
  5. 整形されたDDLを確認してマイグレーションファイルに記載

メリット:

  • カラム定義を視覚的に確認
  • NOT NULL、DEFAULT、UNIQUEの制約を明確化
  • チームメンバーとの共有が容易

場面4:プロジェクトのコーディング規約統一

シーン:新メンバーが参加してコーディング規約がバラバラ

-- メンバーAの書き方(小文字・インデントなし)
select * from users where status='active';

-- メンバーBの書き方(大文字・4スペース)
SELECT
    *
FROM
    users
WHERE
    status = 'active';

手順:

  1. すべてのSQLファイルを順次整形
  2. 方言選択(MySQL)
  3. 統一ルール:大文字・2スペース
  4. プロジェクト全体のSQL規約を統一

メリット:

  • コードレビューの負担軽減
  • 可読性の向上
  • 新メンバーのオンボーディングを円滑化

場面5:Oracle PL/SQLのスラッシュ区切りに対応

シーン:Oracle開発環境でのPL/SQLブロック

BEGIN
  INSERT INTO users (id, name) VALUES (1, 'Test');
  COMMIT;
END; /

問題:

  • sql-formatterはデフォルトで / を行末にくっつけてしまう
  • Oracle SQL*Plusでは / が独立した行にないとエラー

このマクロの対応:

  • postProcessSlashSeparator() 関数で / を独立した行に移動
  • Oracle開発者にとって使いやすい形式で出力
BEGIN
  INSERT INTO users (id, name) VALUES (1, 'Test');
  COMMIT;
END;
/

場面6:複数SQLの一括整形

シーン:複数のクエリが含まれるファイル

SELECT * FROM users WHERE id=1; SELECT * FROM orders WHERE user_id=1; SELECT * FROM products WHERE category='electronics';

手順:

  1. ファイル全体を対象(何も選択しない状態でコマンド実行)
  2. 方言選択(Standard SQL)
  3. 大文字・2スペース
  4. 整形された複数のSQLを確認

出力:

SELECT
  *
FROM
  users
WHERE
  id = 1;

SELECT
  *
FROM
  orders
WHERE
  user_id = 1;

SELECT
  *
FROM
  products
WHERE
  category = 'electronics';

メリット:

  • クエリ間に2行の空行を自動挿入(linesBetweenQueries: 2
  • 複数クエリの視認性向上

TypeScript実装の詳細

実際のコードを紹介します。formatSql.tsファイルに実装しました。

import * as vscode from 'vscode';
import * as path from 'path';
import { format } from 'sql-formatter'; // sql-formatterライブラリをインポート

/**
 * SQL整形ツール(sql-formatterライブラリ使用)
 * 選択されたSQLを整形して新規ファイルに出力
 */
export async function formatSql() {
    const editor = vscode.window.activeTextEditor;
    if (!editor) {
        vscode.window.showWarningMessage('アクティブなエディタがありません');
        return;
    }

    const selection = editor.selection;
    const document = editor.document;

    // 選択範囲がある場合は選択範囲、なければファイル全体を対象
    let sqlText: string;
    if (!selection.isEmpty) {
        sqlText = document.getText(selection);
    } else {
        sqlText = document.getText();
    }

    if (!sqlText || sqlText.trim().length === 0) {
        vscode.window.showWarningMessage('整形対象のSQLがありません');
        return;
    }

    // オプション選択ダイアログを表示
    const options = await showFormatOptionsDialog();
    if (!options) {
        return; // ユーザーがキャンセルした場合
    }

    try {
        // SQL整形を実行
        const formattedSql = formatSqlText(sqlText, options);
        // 新規ファイルに出力
        await createResultFile(formattedSql);
        vscode.window.showInformationMessage('SQL整形が完了しました');
    } catch (error: any) {
        // パースエラーの場合は詳細なメッセージを表示
        if (error.message && error.message.includes('Parse error')) {
            const message =
                'SQL整形に失敗しました。\n\n' +
                '原因:SQLとして認識できない文字が含まれています。\n' +
                '(例:△、●、■などの記号、不正なSQL構文)\n\n' +
                '対処法:\n' +
                '1. SQL以外のテキストやコメントを削除してください\n' +
                '2. 特殊文字(△など)を削除してください\n' +
                '3. SQL構文が正しいか確認してください\n\n' +
                `エラー詳細: ${error.message}`;

            vscode.window.showErrorMessage(message, { modal: true });
        } else {
            vscode.window.showErrorMessage(`SQL整形エラー: ${error.message || error}`);
        }
        console.error('SQL formatting error:', error);
    }
}

/**
 * フォーマットオプション
 */
interface FormatOptions {
    language: 'sql' | 'mysql' | 'postgresql' | 'tsql' | 'plsql' | 'mariadb' | 'db2' | 'redshift';
    keywordCase: 'upper' | 'lower' | 'preserve';
    tabWidth: number;
    useTabs: boolean;
}

/**
 * フォーマットオプション選択ダイアログ
 */
async function showFormatOptionsDialog(): Promise {
    // 1. SQL方言の選択
    const dialectItems = [
        { label: 'Standard SQL', value: 'sql' as const, description: '標準SQL(デフォルト)' },
        { label: 'MySQL / MariaDB', value: 'mysql' as const, description: 'MySQL/MariaDB方言' },
        { label: 'PostgreSQL', value: 'postgresql' as const, description: 'PostgreSQL方言' },
        { label: 'T-SQL (SQL Server)', value: 'tsql' as const, description: 'Microsoft SQL Server' },
        { label: 'PL/SQL (Oracle)', value: 'plsql' as const, description: 'Oracle Database' },
        { label: 'Amazon Redshift', value: 'redshift' as const, description: 'Amazon Redshift' },
        { label: 'IBM DB2', value: 'db2' as const, description: 'IBM DB2' }
    ];

    const selectedDialect = await vscode.window.showQuickPick(dialectItems, {
        placeHolder: 'SQL方言を選択してください',
        ignoreFocusOut: true // ダイアログ外クリックでキャンセルされないようにする
    });

    if (!selectedDialect) {
        return null; // キャンセル
    }

    // 2. キーワードの大文字/小文字
    const caseItems = [
        { label: '大文字 (SELECT, FROM, WHERE)', value: 'upper' as const, description: 'キーワードを大文字に' },
        { label: '小文字 (select, from, where)', value: 'lower' as const, description: 'キーワードを小文字に' },
        { label: '元のまま保持', value: 'preserve' as const, description: '元の大文字/小文字を保持' }
    ];

    const selectedCase = await vscode.window.showQuickPick(caseItems, {
        placeHolder: 'キーワードの大文字/小文字を選択してください',
        ignoreFocusOut: true
    });

    if (!selectedCase) {
        return null; // キャンセル
    }

    // 3. インデント幅
    const indentItems = [
        { label: '2スペース', tabWidth: 2, useTabs: false, description: '推奨' },
        { label: '4スペース', tabWidth: 4, useTabs: false },
        { label: 'タブ', tabWidth: 4, useTabs: true }
    ];

    const selectedIndent = await vscode.window.showQuickPick(indentItems, {
        placeHolder: 'インデント幅を選択してください',
        ignoreFocusOut: true
    });

    if (!selectedIndent) {
        return null; // キャンセル
    }

    return {
        language: selectedDialect.value,
        keywordCase: selectedCase.value,
        tabWidth: selectedIndent.tabWidth,
        useTabs: selectedIndent.useTabs
    };
}

/**
 * SQLテキストを整形
 */
function formatSqlText(sql: string, options: FormatOptions): string {
    const formattedSql = format(sql, {
        language: options.language, // SQL方言を指定
        keywordCase: options.keywordCase, // キーワードの大文字/小文字
        tabWidth: options.tabWidth, // インデント幅
        useTabs: options.useTabs, // タブ使用有無
        linesBetweenQueries: 2 // クエリ間の空行数(複数SQLの場合)
    });

    // Oracle PL/SQL の / を独立した行にする後処理
    // 例: "dual /" → "dual\n/"
    return postProcessSlashSeparator(formattedSql);
}

/**
 * Oracle PL/SQL のスラッシュ区切りを独立した行にする後処理
 */
function postProcessSlashSeparator(sql: string): string {
    // 行末のスラッシュの前に改行を挿入
    // パターン: 任意の文字列 + スペース + / + 改行 → 文字列 + 改行 + / + 改行
    return sql.replace(/\s+\/\s*$/gm, '\n/');
}

/**
 * 結果を新規ファイルに出力
 */
async function createResultFile(formattedSql: string): Promise {
    // 新規ドキュメントを作成(言語モードをSQLに設定)
    const doc = await vscode.workspace.openTextDocument({
        content: formattedSql,
        language: 'sql'
    });

    // 右側に並べて表示
    await vscode.window.showTextDocument(doc, {
        viewColumn: vscode.ViewColumn.Beside,
        preview: false
    });

    // ファイル名を自動生成
    const suggestedName = generateFileName(new Date());

    // 保存ダイアログを表示
    const uri = await vscode.window.showSaveDialog({
        defaultUri: vscode.Uri.file(suggestedName),
        filters: {
            'SQL files': ['sql'],
            'Text files': ['txt'],
            'All files': ['*']
        }
    });

    if (uri) {
        // ファイルを作成
        const edit = new vscode.WorkspaceEdit();
        edit.createFile(uri, { overwrite: true });
        await vscode.workspace.applyEdit(edit);

        // 内容を書き込み
        await vscode.workspace.fs.writeFile(
            uri,
            Buffer.from(formattedSql, 'utf8')
        );

        vscode.window.showInformationMessage(
            `整形済みSQLを保存しました: ${path.basename(uri.fsPath)}`
        );
    }
}

/**
 * 出力ファイル名を生成
 */
function generateFileName(date: Date): string {
    const timestamp = formatDateTimeForFile(date);
    return `formatted_sql_${timestamp}.sql`;
}

/**
 * 日時フォーマット(ファイル名用)
 */
function formatDateTimeForFile(date: Date): string {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    const seconds = String(date.getSeconds()).padStart(2, '0');

    return `${year}${month}${day}_${hours}${minutes}${seconds}`;
}

コードのポイント解説

1. sql-formatterライブラリの活用

import { format } from 'sql-formatter'; // npmパッケージをインポート

const formattedSql = format(sql, {
    language: 'mysql', // SQL方言を指定
    keywordCase: 'upper', // キーワードを大文字に
    tabWidth: 2, // インデント幅
    useTabs: false, // スペースを使用
    linesBetweenQueries: 2 // 複数クエリ間の空行数
});

ポイント:

  • sql-formatterは複数のSQL方言に対応した軽量ライブラリ
  • オプションで細かくカスタマイズ可能
  • パースエラー時には詳細なエラーメッセージを返す

2. オプション選択ダイアログの3段階構成

// 1. SQL方言を選択
const selectedDialect = await vscode.window.showQuickPick(dialectItems, {
    placeHolder: 'SQL方言を選択してください',
    ignoreFocusOut: true // ダイアログ外クリックでキャンセルされない
});

// 2. キーワードケースを選択
const selectedCase = await vscode.window.showQuickPick(caseItems, {
    placeHolder: 'キーワードの大文字/小文字を選択してください',
    ignoreFocusOut: true
});

// 3. インデント幅を選択
const selectedIndent = await vscode.window.showQuickPick(indentItems, {
    placeHolder: 'インデント幅を選択してください',
    ignoreFocusOut: true
});

ポイント:

  • ignoreFocusOut: true でダイアログ外クリックでもキャンセルされない
  • 3段階に分けることでユーザーの選択を明確化
  • 各ステップでnullチェックして早期リターン

3. Oracle PL/SQLのスラッシュ区切り対応

/**
 * Oracle PL/SQL のスラッシュ区切りを独立した行にする後処理
 */
function postProcessSlashSeparator(sql: string): string {
    // 行末のスラッシュの前に改行を挿入
    // パターン: 任意の文字列 + スペース + / + 改行 → 文字列 + 改行 + / + 改行
    return sql.replace(/\s+\/\s*$/gm, '\n/');
}

ポイント:

  • 正規表現 /\s+\/\s*$/gm で行末の / を検出
  • gm フラグで複数行にわたって処理
  • Oracle開発者にとって必須の後処理

4. エラーハンドリングの充実

try {
    const formattedSql = formatSqlText(sqlText, options);
    await createResultFile(formattedSql);
    vscode.window.showInformationMessage('SQL整形が完了しました');
} catch (error: any) {
    // パースエラーの場合は詳細なメッセージを表示
    if (error.message && error.message.includes('Parse error')) {
        const message =
            'SQL整形に失敗しました。\n\n' +
            '原因:SQLとして認識できない文字が含まれています。\n' +
            '(例:△、●、■などの記号、不正なSQL構文)\n\n' +
            '対処法:\n' +
            '1. SQL以外のテキストやコメントを削除してください\n' +
            '2. 特殊文字(△など)を削除してください\n' +
            '3. SQL構文が正しいか確認してください\n\n' +
            `エラー詳細: ${error.message}`;

        vscode.window.showErrorMessage(message, { modal: true });
    } else {
        vscode.window.showErrorMessage(`SQL整形エラー: ${error.message || error}`);
    }
    console.error('SQL formatting error:', error);
}

ポイント:

  • Parse error を検出して専用のエラーメッセージを表示
  • ユーザーに原因と対処法を明示
  • modal: true でエラーダイアログを強調表示

5. 新規ファイル出力とファイル名自動生成

/**
 * 出力ファイル名を生成
 */
function generateFileName(date: Date): string {
    const timestamp = formatDateTimeForFile(date);
    return `formatted_sql_${timestamp}.sql`; // formatted_sql_20260215_103045.sql
}

/**
 * 日時フォーマット(ファイル名用)
 */
function formatDateTimeForFile(date: Date): string {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    const seconds = String(date.getSeconds()).padStart(2, '0');

    return `${year}${month}${day}_${hours}${minutes}${seconds}`; // 20260215_103045
}

ポイント:

  • タイムスタンプ付きファイル名で重複を防止
  • 年月日_時分秒の形式で整理しやすい
  • 保存ダイアログで自動的にファイル名が提案される

6. 右側に並べて表示

// 右側に並べて表示
await vscode.window.showTextDocument(doc, {
    viewColumn: vscode.ViewColumn.Beside, // 右側に表示
    preview: false // プレビューモードを無効化
});

ポイント:

  • vscode.ViewColumn.Beside で元のファイルと並べて表示
  • 元のSQLと整形後のSQLを同時に確認可能
  • preview: false で確実にエディタとして開く

使い方

1. 拡張機能のインストール

git clone https://github.com/xxxxx-sys/my-macros.git
cd my-macros

# 依存関係インストール
npm install --legacy-peer-deps

# sql-formatterライブラリをインストール
npm install sql-formatter

# パッケージ化
npm run compile
npx vsce package

# インストール
code --install-extension my-macros-0.0.7.vsix

2. 基本的な使い方

作業メモ.txt
---
SELECT u.id,u.name,o.order_id FROM users u JOIN orders o ON u.id=o.user_id WHERE u.status='active';

手順:

  1. 整形したいSQLを選択(または何も選択せずファイル全体を対象)
  2. コマンドパレットを開く(Ctrl+Shift+P
  3. "Format SQL" と入力して実行
  4. オプションダイアログで選択:
    • SQL方言:MySQL
    • キーワード:大文字
    • インデント:2スペース
  5. 整形されたSQLが右側に表示される
  6. 保存ダイアログで保存

3. 実践例

シーン1: ログから抽出したSQLを整形

application.log
---
[2026-02-15 10:30:45] Slow Query: SELECT * FROM users WHERE status='active' AND created_at>='2026-01-01';

手順:

  1. SQL部分を選択
  2. コマンド実行
  3. 方言選択(MySQL)
  4. 整形されたSQLを確認

シーン2: 複数のクエリを一括整形

SELECT * FROM users; SELECT * FROM orders; SELECT * FROM products;

手順:

  1. 何も選択せずコマンド実行
  2. 方言選択(Standard SQL)
  3. 整形された3つのSQLが空行区切りで出力

package.jsonの設定

{
  "contributes": {
    "commands": [
      {
        "command": "myMacros.formatSql",
        "title": "Format SQL",
        "category": "My Macros"
      }
    ],
    "keybindings": [
      {
        "command": "myMacros.formatSql",
        "key": "ctrl+shift+f",
        "when": "editorTextFocus"
      }
    ]
  },
  "dependencies": {
    "sql-formatter": "^15.0.0"
  }
}

extension.tsでの登録

import { formatSql } from './macros/formatSql';

export function activate(context: vscode.ExtensionContext) {
    const commands = [
        vscode.commands.registerCommand(
            'myMacros.formatSql',
            formatSql
        ),
        // ... 他のコマンド
    ];

    commands.forEach(command => context.subscriptions.push(command));
}

メリット・デメリット

メリット

✅ 圧倒的な効率化

  • 1キー操作でSQL整形が完了
  • オンラインツールやDBクライアントの立ち上げ不要
  • VSCode上で完結

✅ 複数のSQL方言に対応

  • MySQL、PostgreSQL、Oracle、SQL Server等に対応
  • プロジェクトごとに適切な方言を選択可能

✅ 柔軟なカスタマイズ

  • キーワードの大文字/小文字を選択
  • インデント幅を選択(2スペース、4スペース、タブ)
  • プロジェクトのコーディング規約に合わせて調整可能

✅ 安全性

  • ローカル環境で完結(セキュリティ面で安心)
  • 元のファイルは変更せず新規ファイルに出力
  • エラー時の詳細メッセージで原因を把握

✅ 実務に即した機能

  • Oracle PL/SQLのスラッシュ区切りに対応
  • 複数クエリ間に空行を自動挿入
  • タイムスタンプ付きファイル名で管理しやすい

デメリット

❌ sql-formatterの制限

  • 完全に正しいSQL構文でないと整形できない
  • 特殊な記号(△、●など)が含まれるとパースエラー
  • 方言固有の構文で未対応のものもある

❌ 巨大なSQLファイルはパフォーマンス低下

  • 数万行のSQLファイルは処理に時間がかかる可能性
  • メモリ使用量が増加する場合あり

❌ 出力先が新規ファイル固定

  • 元のファイルを直接置き換える機能はない
  • 必ず新規ファイルに出力される

トラブルシューティング

Q: SQL整形に失敗する

A: SQLとして認識できない文字が含まれている

NG: SELECT * FROM users; -- △注意点
OK: SELECT * FROM users; -- 注意点

解決策:

  • SQL以外のテキストや特殊文字を削除
  • コメント内の記号を確認

Q: 方言を間違えて選択した

A: 再度実行して正しい方言を選択

誤: Standard SQLで整形したがMySQLのバッククォートが消えた
正: MySQLで整形してバッククォートを保持

解決策:

  • MySQLの場合は "MySQL / MariaDB" を選択
  • PostgreSQLの場合は "PostgreSQL" を選択

Q: 整形後のファイルを保存したくない

A: 保存ダイアログでキャンセル

解決策:

  • 整形結果を確認したら保存ダイアログでキャンセル
  • 一時的に確認するだけならそのままタブを閉じる

Q: オプション選択が面倒

A: デフォルト設定を変更したい場合はコードを修正

// デフォルト設定を変更する例
const options = {
    language: 'mysql', // 常にMySQLに固定
    keywordCase: 'upper', // 常に大文字に固定
    tabWidth: 2,
    useTabs: false
};

解決策:

  • よく使う設定をデフォルトにしたい場合はコードを修正

まとめ:SQL整形を1キー操作で実現する快適さ

VSCodeでsql-formatterライブラリを活用したSQL整形ツールを実装しました。

このマクロの強み:

  • 1キー操作でSQL整形が完了
  • 複数のSQL方言に対応(MySQL、PostgreSQL、Oracle等)
  • キーワードケースとインデント幅を選択可能
  • ローカル環境で完結(セキュリティ面で安心)
  • Oracle PL/SQLのスラッシュ区切りに対応
  • 充実したエラーハンドリング

こんな人におすすめ:

  • ログから抽出したSQLを頻繁に分析する
  • 複数のDBを扱うプロジェクトに参加している
  • コーディング規約の統一を効率化したい
  • マウス操作を減らしたい
  • VSCodeで開発環境を完結させたい

特に、本番環境のログから圧縮されたSQLを抽出して分析する場面では、このマクロが大きな威力を発揮します。

手作業で1つずつ改行とインデントを調整していた作業が、たった1回のキー操作で完了します。

「読みづらいSQLに苦労している」という方は、ぜひVSCodeでのSQL整形にお役立てください!

Amazon検索[本 TypeScript]

関連記事

  • VSCodeで正規表現マッチを一括抽出するTypeScriptマクロ
VSCodeで「正規表現マッチ抽出」を実現するTypeScriptマクロ【ログ解析が10倍速くなる】
はじめにサーバーログやアプリケーションログを解析する時、こんな悩みありませんか?2024-02-08 10:00:00 INFO: Server started2024-02-08 10:01:00 ERROR: Connection fa...
  • VSCodeで「選択行による一括置換」を実現するTypeScriptマクロ
VSCodeで「選択行による一括置換」を実現するTypeScriptマクロ【テキスト編集が10倍速くなる】
はじめにテキスト編集で「複数の文字列を一度に置換したい」と思ったことはありませんか?例えば、こんなシーン:変数名を複数箇所で一括変更テーブル定義のデータ型を複数列で一括更新ドキュメント内の用語統一を複数ワードで一括実施VSCodeの標準機能...
  • VSCodeで「任意行までの一括選択・コピー」を実現するTypeScriptマクロ
VSCodeで「任意行までの一括選択・コピー」を実現するTypeScriptマクロ開発【サクラエディタ移行の集大成】
はじめに:テキスト編集における「範囲選択の不便さ」VSCodeで長いログファイルやドキュメントを編集していて、「この行から特定の文字列まで一気にコピーしたい」と思ったことはありませんか?通常のVSCodeでは、以下のような手順が必要です:開...
  • VSCode TypeScriptマクロ開発ガイド【サクラエディタから移行した開発環境構築】
VSCode TypeScriptマクロ開発環境の完全ガイド【セットアップから運用まで】
はじめにVSCodeでカスタムマクロを作成したいけど、どうやって開発環境を構築すればいいか分からない。そんな悩みを持つ方に向けて、TypeScriptでVSCode拡張機能を開発する環境の構築から、実際にマクロを作成して使えるようにするまで...

タグ: #VSCode #TypeScript #マクロ #SQL #データベース #生産性向上 #sql-formatter

コメント