jpskill.com
🛠️ 開発・MCP コミュニティ

sqlite

SQLiteデータベースのコマンドやクエリ、組み込みデータベースのパターンを扱う際に活用するSkill。

📜 元の英語説明(参考)

SQLite database commands, queries, and embedded database patterns. Use when user mentions "sqlite", "sqlite3", "embedded database", "local database", "lightweight db", "sqlite schema", "sqlite backup", "mobile database", or working with .db/.sqlite files.

🇯🇵 日本人クリエイター向け解説

一言でいうと

SQLiteデータベースのコマンドやクエリ、組み込みデータベースのパターンを扱う際に活用するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o sqlite.zip https://jpskill.com/download/6128.zip && unzip -o sqlite.zip && rm sqlite.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/6128.zip -OutFile "$d\sqlite.zip"; Expand-Archive "$d\sqlite.zip" -DestinationPath $d -Force; ri "$d\sqlite.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して sqlite.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → sqlite フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-17
取得日時
2026-05-17
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

SQLite

sqlite3 CLI の基本

sqlite3 myapp.db                    # データベースを開く、または作成する
sqlite3 :memory:                    # インメモリデータベース
sqlite3 myapp.db ".tables"          # ワンショットコマンド
sqlite3 myapp.db < schema.sql       # ファイルから SQL を実行する

シェル内でよく使うドットコマンドです。

.tables                 -- すべてのテーブルを一覧表示する
.schema                 -- すべてのテーブルの CREATE ステートメントを表示する
.schema users           -- 特定のテーブルの CREATE ステートメントを表示する
.headers on             -- 出力に列ヘッダーを表示する
.mode column            -- 列を揃える (他に: csv, json, table, line, tabs)
.width 20 30 10         -- column モードの列幅を設定する
.databases              -- 接続されているデータベースを一覧表示する
.indexes users          -- テーブルのインデックスを一覧表示する
.quit                   -- 終了する

テーブルの作成、挿入、更新、削除

CREATE TABLE IF NOT EXISTS users (
  id INTEGER PRIMARY KEY,  -- rowid のエイリアス、自動インクリメント
  name TEXT NOT NULL,
  email TEXT UNIQUE,
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  user_id INTEGER NOT NULL REFERENCES users(id),
  total REAL NOT NULL,
  status TEXT DEFAULT 'pending',
  created_at TEXT DEFAULT (datetime('now'))
);

INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');

INSERT INTO orders (user_id, total, status)
VALUES (1, 49.99, 'completed'), (1, 25.00, 'pending'), (2, 120.00, 'completed');

UPDATE users SET email = 'newalice@example.com' WHERE id = 1;

DELETE FROM orders WHERE status = 'pending' AND created_at < datetime('now', '-30 days');

RETURNING 句は 3.35.0 以降で利用可能です。sqlite3 --version で確認してください。

クエリ

JOIN、サブクエリ、GROUP BY

SELECT o.id, u.name, o.total
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.status = 'completed';

SELECT u.name, COALESCE(SUM(o.total), 0) AS lifetime_value
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.name;

SELECT * FROM users
WHERE id IN (
  SELECT user_id FROM orders GROUP BY user_id HAVING SUM(total) > 100
);

SELECT status, COUNT(*) AS cnt, ROUND(AVG(total), 2) AS avg_total
FROM orders GROUP BY status HAVING cnt > 1 ORDER BY avg_total DESC;

CTE とウィンドウ関数

WITH monthly AS (
  SELECT strftime('%Y-%m', created_at) AS month, SUM(total) AS revenue
  FROM orders WHERE status = 'completed' GROUP BY 1
)
SELECT month, revenue,
       revenue - LAG(revenue) OVER (ORDER BY month) AS change
FROM monthly;

SELECT user_id, total,
  ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY total DESC) AS rn,
  SUM(total) OVER (PARTITION BY user_id) AS user_total
FROM orders;

出力モード

sqlite3 -header -csv myapp.db "SELECT * FROM users;" > users.csv
sqlite3 -json myapp.db "SELECT * FROM users;"
sqlite3 -header -column myapp.db "SELECT * FROM users;"

シェル内では以下のようにします。

.mode csv               -- カンマ区切り
.mode json              -- オブジェクトの JSON 配列
.mode column            -- 列を揃える
.mode table             -- ASCII テーブルの罫線 (3.36 以降)
.mode line              -- 1 行に 1 つの値
.separator "\t"         -- list モードのカスタム区切り文字

CSV のインポートとエクスポート

# エクスポート
sqlite3 -header -csv myapp.db "SELECT * FROM users;" > users.csv

# 既存のテーブルにインポート
sqlite3 myapp.db <<'EOF'
.mode csv
.import users.csv users
EOF

大量のインポートの場合は、速度のためにトランザクションで囲みます。

BEGIN;
.mode csv
.import large_file.csv target_table
COMMIT;

インデックスと EXPLAIN QUERY PLAN

CREATE INDEX idx_orders_user ON orders(user_id);
CREATE INDEX idx_orders_status_date ON orders(status, created_at);
CREATE UNIQUE INDEX idx_users_email ON users(email);

EXPLAIN QUERY PLAN SELECT * FROM orders WHERE user_id = 1;

出力の重要な点です。

  • SCAN TABLE -- 利用可能なインデックスがなく、すべての行を読み取ります
  • SEARCH TABLE ... USING INDEX -- インデックスが使用されています
  • USING COVERING INDEX -- クエリがインデックスから完全に回答されています
  • USE TEMP B-TREE -- インデックスなしでソートまたは GROUP BY

WAL モードと同時実行性

PRAGMA journal_mode=WAL;   -- 成功すると "wal" を返します
PRAGMA busy_timeout=5000;  -- すぐに失敗する代わりに最大 5 秒待機します
  • 一度に 1 つのライターですが、リーダーはブロックされません
  • データベースの横に -wal および -shm ファイルを作成します
  • チェックポイントを強制する: PRAGMA wal_checkpoint(TRUNCATE);
  • 一度設定すると、接続間で永続化されます

バックアップ

.backup main backup.db          -- オンラインバックアップ (使用中に安全)
.dump                           -- 完全な SQL テキストダンプ
.dump users                     -- 単一のテーブルをダンプする
sqlite3 myapp.db .dump > full_dump.sql    # ファイルにダンプする
sqlite3 restored.db < full_dump.sql       # ダンプから復元する

VACUUM はデータベースファイルを再構築し、スペースを再利用します。

VACUUM;                    -- その場で再構築する
VACUUM INTO 'compact.db';  -- 圧縮されたコピー (3.27 以降)

JSON サポート

SQLite 3.38 以降 (組み込み) または JSON1 拡張機能で利用可能です。

CREATE TABLE events (id INTEGER PRIMARY KEY, data TEXT NOT NULL);

INSERT INTO events (data)
VALUES ('{"type":"click","page":"/home","tags":["mobile","v2"]}');

-- 値を抽出する
SELECT json_extract(data, '$.type') AS event_type FROM events;
SELECT data->>'$.page' AS page FROM events;  -- ->> 演算子 (3.38 以降)

-- 配列を反復処理する
SELECT e.id, j.value AS tag
FROM events e, json_each(json_extract(e.data, '$.tags')) j;

-- JSON を変更する
UPDATE events SET data = json_set(data, '$.processed', true) WHERE id = 1;

-- クエリで JSON を構築する
SELECT json_object('id', id, 'name', name) FROM users;
SELECT json_group_array(json_object('id', id, 'name', name)) FROM users;

全文検索 (FTS5)

CREATE VIRTUAL TABLE articles_fts USING fts5(title, body, content=articles, content_rowid=id);

-- 既存のテーブルからデータを投入する
INSERT INTO articles_fts(rowid, title, body) SELECT id, title, body FROM articles;

-- 論理演算子で検索する
SELECT *, rank FRO
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

SQLite

sqlite3 CLI Basics

sqlite3 myapp.db                    # open or create a database
sqlite3 :memory:                    # in-memory database
sqlite3 myapp.db ".tables"          # one-shot command
sqlite3 myapp.db < schema.sql       # run SQL from file

Common dot-commands inside the shell:

.tables                 -- list all tables
.schema                 -- show CREATE statements for all tables
.schema users           -- show CREATE for specific table
.headers on             -- show column headers in output
.mode column            -- aligned columns (also: csv, json, table, line, tabs)
.width 20 30 10         -- set column widths for column mode
.databases              -- list attached databases
.indexes users          -- list indexes for a table
.quit                   -- exit

Create Tables, Insert, Update, Delete

CREATE TABLE IF NOT EXISTS users (
  id INTEGER PRIMARY KEY,  -- alias for rowid, auto-increments
  name TEXT NOT NULL,
  email TEXT UNIQUE,
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  user_id INTEGER NOT NULL REFERENCES users(id),
  total REAL NOT NULL,
  status TEXT DEFAULT 'pending',
  created_at TEXT DEFAULT (datetime('now'))
);

INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');

INSERT INTO orders (user_id, total, status)
VALUES (1, 49.99, 'completed'), (1, 25.00, 'pending'), (2, 120.00, 'completed');

UPDATE users SET email = 'newalice@example.com' WHERE id = 1;

DELETE FROM orders WHERE status = 'pending' AND created_at < datetime('now', '-30 days');

RETURNING clause available in 3.35.0+. Check with sqlite3 --version.

Queries

Joins, Subqueries, GROUP BY

SELECT o.id, u.name, o.total
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.status = 'completed';

SELECT u.name, COALESCE(SUM(o.total), 0) AS lifetime_value
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.name;

SELECT * FROM users
WHERE id IN (
  SELECT user_id FROM orders GROUP BY user_id HAVING SUM(total) > 100
);

SELECT status, COUNT(*) AS cnt, ROUND(AVG(total), 2) AS avg_total
FROM orders GROUP BY status HAVING cnt > 1 ORDER BY avg_total DESC;

CTEs and Window Functions

WITH monthly AS (
  SELECT strftime('%Y-%m', created_at) AS month, SUM(total) AS revenue
  FROM orders WHERE status = 'completed' GROUP BY 1
)
SELECT month, revenue,
       revenue - LAG(revenue) OVER (ORDER BY month) AS change
FROM monthly;

SELECT user_id, total,
  ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY total DESC) AS rn,
  SUM(total) OVER (PARTITION BY user_id) AS user_total
FROM orders;

Output Modes

sqlite3 -header -csv myapp.db "SELECT * FROM users;" > users.csv
sqlite3 -json myapp.db "SELECT * FROM users;"
sqlite3 -header -column myapp.db "SELECT * FROM users;"

Inside the shell:

.mode csv               -- comma-separated
.mode json              -- JSON array of objects
.mode column            -- aligned columns
.mode table             -- ASCII table borders (3.36+)
.mode line              -- one value per line
.separator "\t"         -- custom separator for list mode

Import and Export CSV

# Export
sqlite3 -header -csv myapp.db "SELECT * FROM users;" > users.csv

# Import into existing table
sqlite3 myapp.db <<'EOF'
.mode csv
.import users.csv users
EOF

For large imports, wrap in a transaction for speed:

BEGIN;
.mode csv
.import large_file.csv target_table
COMMIT;

Indexes and EXPLAIN QUERY PLAN

CREATE INDEX idx_orders_user ON orders(user_id);
CREATE INDEX idx_orders_status_date ON orders(status, created_at);
CREATE UNIQUE INDEX idx_users_email ON users(email);

EXPLAIN QUERY PLAN SELECT * FROM orders WHERE user_id = 1;

Key things in the output:

  • SCAN TABLE -- no usable index, reads every row
  • SEARCH TABLE ... USING INDEX -- index is being used
  • USING COVERING INDEX -- query answered entirely from index
  • USE TEMP B-TREE -- sort or GROUP BY without index

WAL Mode and Concurrency

PRAGMA journal_mode=WAL;   -- returns "wal" on success
PRAGMA busy_timeout=5000;  -- wait up to 5s instead of failing immediately
  • One writer at a time, but readers are not blocked
  • Creates -wal and -shm files alongside the database
  • Force checkpoint: PRAGMA wal_checkpoint(TRUNCATE);
  • Set once; persists across connections

Backup

.backup main backup.db          -- online backup (safe during use)
.dump                           -- full SQL text dump
.dump users                     -- dump a single table
sqlite3 myapp.db .dump > full_dump.sql    # dump to file
sqlite3 restored.db < full_dump.sql       # restore from dump

VACUUM rebuilds the database file, reclaiming space:

VACUUM;                    -- rebuild in place
VACUUM INTO 'compact.db';  -- compacted copy (3.27+)

JSON Support

Available in SQLite 3.38+ (built-in) or via the JSON1 extension:

CREATE TABLE events (id INTEGER PRIMARY KEY, data TEXT NOT NULL);

INSERT INTO events (data)
VALUES ('{"type":"click","page":"/home","tags":["mobile","v2"]}');

-- Extract values
SELECT json_extract(data, '$.type') AS event_type FROM events;
SELECT data->>'$.page' AS page FROM events;  -- ->> operator (3.38+)

-- Iterate over arrays
SELECT e.id, j.value AS tag
FROM events e, json_each(json_extract(e.data, '$.tags')) j;

-- Modify JSON
UPDATE events SET data = json_set(data, '$.processed', true) WHERE id = 1;

-- Build JSON in queries
SELECT json_object('id', id, 'name', name) FROM users;
SELECT json_group_array(json_object('id', id, 'name', name)) FROM users;

Full-Text Search (FTS5)

CREATE VIRTUAL TABLE articles_fts USING fts5(title, body, content=articles, content_rowid=id);

-- Populate from existing table
INSERT INTO articles_fts(rowid, title, body) SELECT id, title, body FROM articles;

-- Search with boolean operators
SELECT *, rank FROM articles_fts WHERE articles_fts MATCH 'database AND performance' ORDER BY rank;

-- Highlight matches
SELECT highlight(articles_fts, 1, '<b>', '</b>') FROM articles_fts WHERE articles_fts MATCH 'sqlite';

-- Keep in sync with triggers
CREATE TRIGGER articles_ai AFTER INSERT ON articles BEGIN
  INSERT INTO articles_fts(rowid, title, body) VALUES (new.id, new.title, new.body);
END;
CREATE TRIGGER articles_ad AFTER DELETE ON articles BEGIN
  INSERT INTO articles_fts(articles_fts, rowid, title, body)
  VALUES ('delete', old.id, old.title, old.body);
END;

Date and Time Functions

SQLite stores dates as TEXT, REAL, or INTEGER. Built-in functions handle ISO-8601 strings:

SELECT datetime('now');                              -- 2025-01-15 08:30:00
SELECT date('now', '-7 days');                       -- 7 days ago
SELECT strftime('%Y-%m', 'now');                     -- current year-month
SELECT strftime('%s', 'now');                        -- unix timestamp
SELECT datetime('2025-01-15', '+3 months', '-1 day'); -- date arithmetic
SELECT julianday('now') - julianday(created_at) AS days_old FROM users;

Attach Multiple Databases

ATTACH DATABASE 'archive.db' AS archive;

SELECT * FROM main.users u JOIN archive.orders o ON o.user_id = u.id;

INSERT INTO archive.orders SELECT * FROM main.orders WHERE created_at < '2024-01-01';
DELETE FROM main.orders WHERE created_at < '2024-01-01';

DETACH DATABASE archive;

Pragmas

-- Performance
PRAGMA journal_mode=WAL;
PRAGMA synchronous=NORMAL;       -- faster writes (safe with WAL)
PRAGMA cache_size=-64000;        -- 64MB page cache (negative = KB)
PRAGMA mmap_size=268435456;      -- memory-map up to 256MB
PRAGMA temp_store=MEMORY;        -- temp tables in memory

-- Integrity
PRAGMA foreign_keys=ON;          -- enforce foreign keys (off by default!)
PRAGMA integrity_check;          -- full database integrity scan

-- Info
PRAGMA table_info(users);        -- column details
PRAGMA index_list(users);        -- indexes on a table
PRAGMA compile_options;          -- build-time options (check for JSON, FTS5)

Common Patterns

Config Key-Value Store

CREATE TABLE config (
  key TEXT PRIMARY KEY, value TEXT,
  updated_at TEXT DEFAULT (datetime('now'))
);
INSERT OR REPLACE INTO config (key, value) VALUES ('theme', 'dark');
SELECT value FROM config WHERE key = 'theme';

Local Cache with Expiry

CREATE TABLE cache (key TEXT PRIMARY KEY, value TEXT, expires_at TEXT);

INSERT OR REPLACE INTO cache (key, value, expires_at)
VALUES ('api:/users', '{"data":[...]}', datetime('now', '+1 hour'));

SELECT value FROM cache WHERE key = 'api:/users' AND expires_at > datetime('now');
DELETE FROM cache WHERE expires_at <= datetime('now');

CLI Tool Storage

CREATE TABLE history (
  id INTEGER PRIMARY KEY, command TEXT NOT NULL,
  args TEXT, exit_code INTEGER,
  ran_at TEXT DEFAULT (datetime('now'))
);
SELECT command, args, ran_at FROM history ORDER BY ran_at DESC LIMIT 20;
SELECT command, COUNT(*) AS cnt FROM history GROUP BY command ORDER BY cnt DESC;

Test Fixtures

sqlite3 test.db < schema.sql && sqlite3 test.db < seed.sql
# In-memory: Python sqlite3.connect(":memory:") / Node new Database(":memory:")
BEGIN;
INSERT INTO users (id, name, email) VALUES
  (1, 'Test User', 'test@example.com'),
  (2, 'Admin', 'admin@example.com');
INSERT INTO orders (user_id, total, status) VALUES
  (1, 99.99, 'completed'), (2, 50.00, 'pending');
COMMIT;