SQLite Editorを180行で作り、ブラウザ版を730行で作り直した
はじめに
特許データ、判例PDF、行政データベース——仕事で大量のSQLiteファイルを扱う。テーブルを開いてカラムを選び、フィルタをかけて中身を確認する。それだけの作業に重いIDEは要らない。
最初にStreamlit + pandas + sqlite3で作った。180行、1時間で完成。毎日使った。次に共有しようとして問題に気づいた。
Streamlit版:180行で完成
Streamlitのst.data_editorが仕事の大半をこなす。仮想スクロール、ソート、インライン編集、横長テーブル対応——すべて無料で付いてくる。
主要機能: ・サイドバーにテーブル一覧(行数表示付き) ・カラム選択(multiselect)とピン固定(sticky left) ・WHERE句を直接入力するフィルタ ・ソート、LIMIT、CSV出力 ・セル編集 → PKベースでUPDATE文を自動生成・保存 ・クエリ実行時間の表示 ・PKがないテーブルはリードオンリー
コアのクエリ構築部分:
display = sel_cols or col_names
q = f'SELECT {", ".join(f\'"{c}"\' for c in display)} FROM "{S.tbl}"'
if where.strip():
q += f" WHERE {where.strip()}"
if order:
q += f' ORDER BY "{order}" {"DESC" if desc else "ASC"}'
q += f" LIMIT {limit}"
st.data_editorで表示し、編集があればPK列をキーにしてUPDATEを発行する。179行。
共有の壁
Streamlitアプリを公開すると、ユーザーは.dbファイルをサーバーにアップロードすることになる。自分専用なら問題ないが、公開ツールとして他人のデータベースを受け取るのは望ましくない。
解決策:sql.js——SQLiteをWebAssemblyにコンパイルしたライブラリ。すべてブラウザ内で完結する。ファイルはサーバーに送信されない。
WASM版:単一HTMLファイル、730行
代償としてst.data_editorを失い、UIを自前で構築することになった。JS部分は約520行、CSSが124行、HTML構造が約90行。合計731行の単一HTMLファイル。外部フレームワークはゼロ。唯一の依存はsql.js。
実装した機能: ・ドラッグ&ドロップで.dbファイルを開く ・カラム選択ドロップダウン + ピン固定(position: stickyで左端に固定) ・セルのインライン編集 → 変更済みDBをダウンロード ・WHERE句を直接入力(デバウンス付き自動実行) ・ソート、ページネーション(OFFSET/LIMIT)、CSV出力 ・ダーク/ライトモード(localStorage永続化) ・PWA対応(Service Worker + manifest.json)でオフライン動作 ・モバイルレスポンシブ(ハンバーガーメニュー) ・キーボードショートカット(Ctrl+S保存、Ctrl+E CSV出力、Ctrl+Enter再実行)
カラムピン固定の実装
横長テーブルでID列やキー列を見失わないための機能。CSSのposition: stickyを動的に適用する。
function applyPinning() {
if (!S.pinnedCols.length) return;
const ths = gridHead.querySelectorAll('th');
const displayCols = S.result.columns;
const pinIndices = S.pinnedCols.map(c => displayCols.indexOf(c)).filter(i => i >= 0);
let cumLeft = 0;
pinIndices.forEach((colIdx, i) => {
const th = ths[colIdx];
const w = th.offsetWidth;
th.style.position = 'sticky';
th.style.left = cumLeft + 'px';
th.style.zIndex = 3;
gridBody.querySelectorAll('tr').forEach(tr => {
const td = tr.children[colIdx];
td.style.position = 'sticky';
td.style.left = cumLeft + 'px';
td.style.zIndex = 1;
});
cumLeft += w;
});
}
各ピン列のoffsetWidthを累計し、左からの位置を動的に計算する。ヘッダーはz-index: 3、ボディはz-index: 1で重なりを制御。最後のピン列にはbox-shadowを付けて境界を明示する。
インライン編集とセーブ
contentEditableでセルを編集可能にし、blur時に変更を検知する。PKがないテーブルではcontentEditableを付与しない(リードオンリー)。
const editable = hasPk && !S.pkColumns.includes(columns[ci]);
if (editable) {
td.contentEditable = true;
td.dataset.ri = ri;
td.dataset.col = columns[ci];
td.dataset.orig = val === null ? '\x00NULL\x00' : String(val);
td.dataset.pk = JSON.stringify(pkIndices.map(i => row[i]));
}
保存時はpendingEditsをイテレートし、PKベースのUPDATE文を発行する。数値カラムには型強制(coerce関数)を適用し、INTEGERカラムに文字列が入ることを防ぐ。
キーボードショートカット
- Ctrl+S: 編集を保存
- Ctrl+E: CSV出力
- Ctrl+Enter: クエリ再実行
- Enter: 下のセルへ移動
- Tab/Shift+Tab: 隣のセルへ移動
- Escape: 編集をキャンセル
セル間の移動はTab/Enter/Escapeでスプレッドシートライクに操作できる。ペースト時はプレーンテキストのみに制限し、HTMLタグの混入を防ぐ。
なぜこれが存在しなかったのか
探した限り、カラムピン固定とインライン編集ができる単一ファイルのSQLiteエディタは見つからなかった。既存ツールはフル機能のIDE(DB Browser for SQLiteなど)か、SQL学習用のプレイグラウンドのどちらか。データベースをさっと開いて中身を確認・編集したい人向けの中間地帯が空いていた。
まとめ
Streamlit版とWASM版の両方を維持している。Streamlit版(180行)はローカルワークフロー用、WASM版(730行)はブラウザだけで完結する共有ツール。2回作ることになったが、それぞれの用途に最適化されている。
フレームワークも外部依存もない。右クリック → 名前を付けて保存でオフライン利用可能。
試してみてください:https://media.patentllm.org/static/apps/103-sqlite-editor.html