「SQLって何から始めたらいいの?」
プログラミング学習者の多くがここで詰まります。「とりあえずMySQLをインストールして……」と始めた瞬間に、環境構築の壁にぶつかって諦める。データベースを学ぶ前に別の問題で挫折する、そのパターンを何とかしたかった。
ブラウザを開くだけでSQLを打てたら、その壁はなくなる。
それが SQL道場 の出発点です。CmdDojo(Linuxコマンド練習)の設計パターンを流用し、SQLite WASMでバックエンドなしのSQLエディタを実装しました。
#完了した教材を、もう一度練習する
完了画面から同じ教材を最初から再挑戦できるようにしました。途中の画面でも確認付きでやり直せます。今回の課題と得点だけを戻し、過去の最高完了得点と他教材の記録は残します。教材DBも初期化します。再挑戦中に再読み込みしても、以前の完了状態へ戻らず今回の続きから再開します。
得点は保存済みの合計に加算せず、今回達成した課題の配点から計算します。同じ課題の重複完了や未知の課題では増えません。全課題達成時に結果と最高完了得点を一度に確定します。現在の採点は誤答やヒントで減点せず、全課題を正解すれば配点合計に達します。低い点数の完了結果を作るために、架空の減点ルールは追加していません。
以前の保存キーと外側の形式を維持し、旧完了記録の得点を最高完了得点へ引き継ぎます。課題順序・配点・保存形式・新しい教材revisionを検査し、不正データは元のまま残して操作を止めます。個別初期化と全体初期化の影響は確認文で区別し、壊れた記録の最高点まで保持できるとは約束しません。保存容量不足では画面内だけの状態であることを表示して再試行できます。再挑戦の保存に失敗した場合は、以前の状態を消して成功したふりをしません。
既存の暗いSQLコンソールと青い操作色を維持し、保存状況と再挑戦は小さなパネルへまとめています。SQL入力・結果・教材DBへの変更は保存しないので、再読み込みではDBが初期化されます。端末間同期・複数タブ競合の解決・学習結果の認証ではありません。
#🎯 なぜSQL練習サービスなのか
#CmdDojoからの発想
CmdDojoを作ったとき、「このアーキテクチャ、SQLにも使えるんじゃないか」と気づきました。
CmdDojoの構造は:
- 仮想ファイルシステム(TypeScriptで実装)
- コマンドエンジン(文字列 → 実行 → 出力)
- 採点ロジック(validate関数)
- レッスン・コース定義
SQL道場はこれを:
- サンプルDB(sql.jsで初期化)
- SQLエンジン(文字列 → sql.jsで実行 → 結果テーブル)
- 採点ロジック(結果セットを検証するvalidate関数)
- レッスン・コース定義
に置き換えるだけでした。
#SEO的な狙い
「SQL 練習」「SQL 入門」「SQLite チュートリアル」「基本情報 データベース」——これらは検索需要が大きく、かつ既存の学習コンテンツがやや古いものが多い。
CmdDojo・SQL道場・(将来的には)Git道場を合わせて「道場シリーズ」としてブランド化できれば、「ブラウザで学べる技術練習サイト」という認知を作れます。
#🏗️ 技術選定:なぜsql.jsか
SQLをブラウザで動かす選択肢はいくつかあります。
| 選択肢 | メリット | デメリット |
|---|---|---|
| sql.js | 安定・実績あり・SQLite互換 | WASMファイル(650KB)が重い |
@sqlite.org/sqlite-wasm | 公式・最新 | セットアップが複雑 |
absurd-sql | IndexedDB永続化できる | メンテが止まっている |
| バックエンドDB | フル機能 | サーバーコストが発生 |
sql.jsを選んだ理由は実績と安定性です。GitHubスターも多く、Next.jsとの組み合わせ事例も豊富。WASMファイルのサイズは初回ロード時のみ気になる程度で、一度キャッシュされれば問題ありません。
#Next.jsでのWASM配信方法
sql.jsはWASMファイルを動的に読み込みます。Next.jsではこれを public/ に配置して配信しました。
# インストール後にWASMをpublicにコピー
cp node_modules/sql.js/dist/sql-wasm.wasm public/sql-wasm/// SqlEngine.ts
const SQL = await initSqlJs({
locateFile: => "/sql-wasm/sql-wasm.wasm"
})locateFile で配信パスを明示的に指定するのがポイント。デフォルトのCDNパスはNext.jsのCSPと衝突することがあります。
#🔧 実装の詳細
#DBインスタンスの管理
sql.jsのDBインスタンスをReactのstateに入れると、再レンダリングのたびに問題が起きます。ZustandのstoreにDB objectを入れるのもNGです(シリアライズできないため)。
解決策はモジュールレベル変数:
// SqlEngine.ts — モジュールスコープでシングルトン管理
let dbInstance: any = null
export async function getDatabase(initSql: string): Promise<any> {
const SQL = await getSql
if (!dbInstance) {
dbInstance = new SQL.Database
dbInstance.run(initSql)
}
return dbInstance
}
export function resetDatabase {
dbInstance = null // レッスン切り替え時にリセット
}Reactのライフサイクルに依存せず、モジュールの初期化時に一度だけDBを作って使い回します。
#結果の表示形式
CmdDojoはテキスト出力でしたが、SQL道場はテーブル表示が必要です。sql.jsの実行結果は:
interface SqlResult {
columns: string[]
values: (string | number | null)[][]
rowCount: number
}この構造をそのまま <table> タグに展開します。ポイントは overflow-x: auto のラッパー — 列が多い場合に横スクロールできるようにしました。
#採点ロジック
CmdDojoでは「入力文字列が正しいか」を検証していましたが、SQLは「同じ結果を出すSQLが複数ある」ため、文字列一致では採点できません。
解決策:実行結果を検証するvalidate関数
// lesson-01-select.ts
{
id: "sql-01-ex-02",
instruction: "name と price の列だけ取得してください",
validate: (result) => {
if (!result) return false
return (
result.rowCount === 10 &&
result.columns.length === 2 &&
result.columns.includes("name") &&
result.columns.includes("price")
)
},
}SELECT name, price FROM products でも SELECT price, name FROM products でも正解にできます。「行数・列数・含まれるカラム名」で柔軟に判定しています。
#サンプルデータベースの設計
学習に使うデータは「身近で直感的」であることが重要です。
-- 3テーブル構成
products (商品: id, name, category, price, stock)
customers (顧客: id, name, city, age)
orders (注文: id, customer_id, product_id, quantity, order_date)電子機器・家具・文房具の商品、東京・大阪・名古屋の顧客。GROUP BY を練習するとき「カテゴリ別の平均価格」「都市別の顧客数」といった自然な集計ができます。JOIN練習では「顧客の注文一覧」が作れる。
#🎨 UIデザインの考え方
#CmdDojoとの差別化
SQL道場とCmdDojoは同じ「左:解説 / 右:操作画面」のレイアウトですが、操作パネルが全く異なります。
| 項目 | CmdDojo | SQL道場 |
|---|---|---|
| 入力欄 | 1行のターミナル入力 | 複数行テキストエリア |
| 出力 | テキスト行 | 表(テーブル) |
| 実行 | Enterキー | Ctrl+Enter / ボタン |
| アクセントカラー | Teal (#4ec9b0) | Blue (#4db6f0) |
SQLは複数行で書くことが多いので、<textarea> にしました。Ctrl+Enterで実行できるようにして、キーボードから手を離さずに操作できます。
┌────────────────┬────────────────────────┐
│ │ SELECT name, price │
│ 解説パネル │ FROM products │
│ │ WHERE price > 10000; │
│ コマンド概念 ├────────────────────────┤
│ オプション表 │ ▶ 実行 │
│ 例文 ├────────────────────────┤
│ │ name | price │
│ │ ノートPC | 98000 │
│ │ モニター | 45000 │
│ ├────────────────────────┤
│ │ 練習問題トラッカー │
└────────────────┴────────────────────────┘#⚡ 開発スピードについて
SQL道場はCmdDojoから3日後に公開しました。
これが可能だった理由:
- アーキテクチャの再利用 — レッスン・コース・採点の構造はほぼ同じ
- CSSの流用 — cmd-dojo.module.cssをベースに色を変えるだけ
- ページ構成の複製 — ルーティング・layout・generateMetadataのパターンが確立済み
- AIエージェントによる実装 — 設計意図を伝えれば、実装はエージェントが担当
「1つ作ると2つ目は半分の時間で作れる」を実感しました。
#📊 コース設計の考え方
#入門コースの5レッスン構成
| レッスン | テーマ | 学べること |
|---|---|---|
| 01 | SELECT の基本 | 全件取得・列指定 |
| 02 | WHERE で絞り込む | 条件指定・AND/OR/LIKE/BETWEEN |
| 03 | 並び替えと件数制限 | ORDER BY / LIMIT |
| 04 | 集計関数 | COUNT / SUM / AVG / MAX / MIN |
| 05 | GROUP BY | グループ集計・HAVING |
この5つは、データを取得・絞り込み・集計するための基礎です。業務全体の何割をカバーするかは、仕事内容やデータ構造によって変わります。
#現在の教材 (2026年10月)
入門・中級・上級・実務データ分析の4コース、21レッスン・合計62問を公開しています。JOIN・サブクエリ・ウィンドウ関数に加え、実務データ分析では90日分・563行の練習用売上データで月次集計や構成比、店舗別上位抽出を練習できます。
教材一覧はSQL練習問題一覧、実務向けの入口は実務データ分析SQLコースです。SQLiteで動作する教材であり、他のデータベース製品と構文や関数がすべて同じという意味ではありません。
#利用料金と現在の提供範囲
公開中の4コースは無料です。有料コースや法人プランを販売しているわけではありません。広告収益や学習成果も、教材を公開しただけで保証できるものではありません。
環境構築をせずにクエリを実行できるため、授業や自主学習の補助として使えます。一方、実際の業務データを接続する機能や、データベース管理者向けの運用研修は提供していません。
#複数行SQLの編集体験を改善
2026年10月の改善では、行番号、SQLの色分け、選択した複数行の字下げ・解除、元に戻す・やり直す操作を追加しました。入力欄を大型エディタライブラリに置き換えず、標準のtextareaに表示専用の色分けを重ねています。追加の有料APIや依存パッケージは使っていません。
Tabで字下げ、Shift+Tabで解除、CtrlまたはCommand+Enterで実行できます。入力欄からキーボードで移動する場合はEscを押してからTabを押します。スマートフォン向けには字下げと編集履歴のボタンも用意しました。
表示側はSQLを実行せず、HTMLとして解釈しません。文字列、コメント、未閉じの引用符、日本語の入力をそのまま維持します。入力欄の横・縦スクロールに色分けと行番号を同期させ、日本語変換中は実行や字下げを割り込ませない設計にしました。色分けは構文エラー検査やSQLの自動修正ではありません。
検証では62問の判定に加え、SQLiteの実行結果、複数行の選択と取り消し、長いSQLのスクロールを確認します。日本語変換中のイベントを使った自動テストも行いますが、すべてのOS・IME製品の実操作を保証するものではありません。
#実務の順位は、集計する単位から考える
店舗別ランキングの3問を追加しました。売上明細の大きな1行ではなく、月・店舗・商品で合計してから順位を付け、最後に上位を抽出します。全体のLIMITで3行に切るだけでは、店舗ごとの上位3商品にはなりません。
同率順位には既存データの2026-03-15を使います。東京本店の商品5と10はともに105,000円、商品1は98,000円です。RANKでは1・1・3、DENSE_RANKでは1・1・2となるため、金額の上位2段階を残すには後者を選びます。順位に商品IDを混ぜると同率がなくなる一方、件数固定のROW_NUMBERには決め方として役立ちます。順位の基準と表示順を分けて説明しました。定義はSQLite公式のウィンドウ関数で確認できます。
新3問の採点は、件数だけでなく全行の店舗・商品・金額・順位、指定した列名と表示順を比較します。期待値はSQLの模範解答からコピーせず、元の563行をJavaScriptで集計・順位付けした結果と照合しました。店舗・月の区切り忘れ、RANKとDENSE_RANKの取り違え、同率への余計なキー、集計前のLIMIT、1セルだけの誤りも検査します。別構成でも同じ結果なら正解で、SQL構文そのものを強制する採点ではありません。
#全62問を、結果全体で採点する
順位以外には、件数や一部の値だけで判定する問題が残っていました。例えば1,000〜10,000円の商品を求める課題で、98,000円の商品を含む2行でも正解になっていました。残る56問も、練習データをJavaScriptで絞り込み・結合・集計した期待値と、全行・全列を比較する方式へ揃えました。
順序を指定していない問題は行を並べ替えても正解です。集計列の別名が指定されていなければ別名も自由にし、指定されている列名は確認します。判定対象の列と行順は課題の下にも表示します。NULLを0や文字列に置き換える答え、重複行で欠けた行を埋める答え、値が違うのに件数だけ合う答えは通しません。小数の計算誤差は絶対値0.0000001以内に限り許容し、丸めの桁数が曖昧だった3問も明確にしました。
全62問の正解例に加え、1,774セルそれぞれの改変、行の欠落・追加、誤った集計・結合・移動平均・構成比・前月比と、別構成の正しいSQLを検証します。これは固定の練習データに対する結果照合です。同じ結果を返すハードコードの検出や、未知のデータでもそのSQLが正しいことの証明、過去の保存成績の再認証はしません。
#まとめ
#重いSQLを中止して、教材だけを戻す
SQL実行は専用のWeb Workerへ移しました。処理が終わらないSQLでも画面側の操作を続けられ、「実行を中止」でWorkerを終了して教材DBを初期化できます。「教材リセット」も同じ教材復元を行います。UPDATEやDROPなどで変更した教材データは失われますが、入力SQL・編集履歴・学習進捗は残します。別レッスンへ移るときはそのレッスン用に教材を初期化します。
初期化失敗は待機表示のまま放置せず、画面に理由と再試行を出します。起動は60秒、実行は30秒で打ち切り、結果が1000行を超える場合は切り捨てて採点せずエラーにします。SQLエラーは訂正してそのまま再実行できます。BLOB表示は非対応を明示します。端末のメモリを無制限に保護できる仕組みや、あらゆるSQLの安全性を保証するものではありません。
SQL道場は「環境構築なしにSQLを練習できる」という明確なUXと、SEO的に積み上がるコンテンツ構造を持ったサービスです。
技術的にはsql.js(SQLite WASM)によるバックエンド不要の設計と、実行結果を検証するvalidate関数による柔軟な採点ロジックが肝です。
CmdDojoのアーキテクチャを流用することで短期間での開発を実現しました。「道場シリーズ」として展開することで、ブランドとSEO資産を積み重ねていく予定です。
[ ./next_action ]
読んだら、 SQL道場 を実際に動かす。
この開発ログは SQL道場 をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。