- SQL
SQLiteの使い方|コマンド早見表とPython連携を実行例で解説
SQLiteとは、サーバーを立てずにファイル1つで動く、軽量なリレーショナルデータベースです。 インストールしてすぐ sqlite3 ファイル名 で使い始められ、Python には標準モジュール sqlite3 が同梱されているので、追加のインストールなしにプログラムから扱えます。
この記事では、sqlite3 コマンド(ドットコマンド)の早見表から、CRUD・データ型・STRICT テーブル・UPSERT・RETURNING・JSON 関数・WAL モード・CSV 入出力・Python 連携・バックアップまでを、実際に実行した出力つきで解説します。検証環境は Ubuntu 24.04 の apt-get install -y sqlite3(SQLite 3.45.1)と Python 3.11(同梱ライブラリも 3.45.1)です。公式サイトの最新版は 3.53.4(2026-07-24) で、本文で扱う機能はすべて 3.45 以前に入っているため、どちらでも同じように動きます。
SQLite ドットコマンドの早見表
CLI(sqlite3)の「ドットコマンド」は SQL ではなく、; なしで 1 行に 1 つ書きます。この表の分を覚えれば日常の操作は足ります。
| ドットコマンド | 何をするか | 例 |
|---|---|---|
.open ファイル |
DB ファイルを開く(なければ作成) | .open shop.db |
.tables |
テーブル一覧 | .tables |
.schema [表] |
CREATE 文(定義)を表示 | .schema items |
.indexes [表] |
インデックス一覧 | .indexes items |
.headers on|off |
列名の表示を切り替え | .headers on |
.mode 形式 |
出力形式(box / column / csv / json / line / list / markdown / table) | .mode box |
.nullvalue 文字 |
NULL の表示文字を指定 | .nullvalue (null) |
.import --csv [--skip 1] ファイル 表 |
CSV を取り込む | .import --csv --skip 1 items.csv items |
.output ファイル / .output stdout |
出力先をファイルに切り替え/戻す | .output out.csv |
.read ファイル |
SQL ファイルを実行 | .read init.sql |
.dump [表] |
DB 全体(または表)を SQL 文で書き出す | .output all.sql → .dump |
.backup ファイル |
オンラインバックアップ(稼働中でも一貫した複製) | .backup shop_backup.db |
.restore ファイル |
バックアップから復元 | .restore shop_backup.db |
.timer on |
各 SQL の実行時間を表示 | .timer on |
.help [名前] |
ヘルプ(3.45.1 で 62 個のコマンド) | .help mode |
.quit / .exit |
終了(Ctrl+D でも可) | .quit |
SQL 側でよく使う機能も、対応バージョンとあわせて一覧にします。
| やりたいこと | 書き方 | 対応バージョン |
|---|---|---|
| 追加・更新・削除の結果を受け取る | INSERT ... RETURNING id |
3.35.0(2021-03) |
| あれば更新、なければ追加 | INSERT ... ON CONFLICT(col) DO UPDATE SET ... |
3.24.0(2018-06) |
| 型を厳密にチェック | CREATE TABLE t (...) STRICT |
3.37.0(2021-11) |
| JSON から値を取り出す | col ->> '$.key'、json_extract(col, '$.key') |
演算子は 3.38.0(2022-02) |
| JSON 配列を行に展開 | FROM t, json_each(t.col, '$.items') |
3.38.0 から既定で組み込み |
| 列を削除 | ALTER TABLE t DROP COLUMN c |
3.35.0 |
| 読み書きの同時実行を良くする | PRAGMA journal_mode = WAL; |
3.7.0(2010-07) |
| 最小サイズのバックアップ | VACUUM INTO 'copy.db'; |
3.27.0(2019-02) |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「SQLiteの使い方|コマンド早見表とPython連携を実行例で解説」
SQLiteの特徴とインストール(バージョンの確認)
SQLite は「ライブラリ」であり、アプリのプロセス内でファイルを直接読み書きします。 PostgreSQL や MySQL のようなサーバープロセス、ユーザー管理、ポート番号がなく、DB は .db ファイル 1 つです。スマートフォンやブラウザ、各種アプリの内部ストレージとして広く使われています。
CLI は Ubuntu / Debian なら sudo apt-get install -y sqlite3、macOS は標準で同梱、Windows は公式サイトの「Precompiled Binaries」の zip を展開して使います。Python は標準モジュール sqlite3 をそのまま使えます。
$ sqlite3 --version
3.45.1 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257ccalt1 (64-bit)
$ python3 -c "import sqlite3; print(sqlite3.sqlite_version)"
3.45.1
パッケージ管理で入るバージョンは公式最新版より前のことが多いので、使いたい機能の対応バージョンを上の表で確認しておくと安心です。
sqlite3 コマンド(CLI)を実行結果で確認する
DB ファイルを作り、テーブルを作成する
sqlite3 ファイル名 で起動すると、ファイルがなければ新しく作られます。SQL は ; で終わらせ、ドットコマンドは ; なしで書きます。
$ sqlite3 shop.db
sqlite> CREATE TABLE items (
...> id INTEGER PRIMARY KEY,
...> name TEXT NOT NULL,
...> price INTEGER NOT NULL DEFAULT 0,
...> stock INTEGER NOT NULL DEFAULT 0
...> );
sqlite> INSERT INTO items (name, price, stock) VALUES ('notebook', 120, 50), ('pen', 100, 200), ('sticky', 250, 30);
sqlite> .tables
items
sqlite> .schema items
CREATE TABLE items (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price INTEGER NOT NULL DEFAULT 0,
stock INTEGER NOT NULL DEFAULT 0
);
出力形式を切り替える(.headers / .mode)
既定の出力は | 区切りで列名なしです。人が読むなら .headers on と .mode box(または column)、プログラムに渡すなら csv や json に切り替えます。.mode markdown なら Markdown の表がそのまま出ます。
sqlite> SELECT * FROM items;
1|notebook|120|50
2|pen|100|200
3|sticky|250|30
sqlite> .headers on
sqlite> .mode box
sqlite> SELECT id, name, price FROM items WHERE price >= 120;
┌────┬──────────┬───────┐
│ id │ name │ price │
├────┼──────────┼───────┤
│ 1 │ notebook │ 120 │
│ 3 │ sticky │ 250 │
└────┴──────────┴───────┘
ファイル出力・SQL ファイル実行・ダンプ・バックアップ
sqlite> .output out.csv -- 以降の出力を out.csv へ
sqlite> .mode csv
sqlite> SELECT * FROM items;
sqlite> .output stdout -- 画面に戻す
sqlite> .read init.sql -- SQL ファイルを実行
sqlite> .dump -- CREATE TABLE と INSERT 文が BEGIN〜COMMIT に包まれて出力される
sqlite> .backup shop_backup.db
.dump は「SQL 文としての書き出し」、.backup は「ファイルとしての複製」です。別のマシンやバージョンへ移すなら .dump、同じ環境の複製なら .backup が手軽です。
対話モードに入らず 1 行で実行する
シェルスクリプトや cron からは、引数に SQL を渡します。-header -column -json -csv で出力形式も指定でき、ドットコマンドは引数を分けて渡します。
$ sqlite3 -json shop.db "SELECT id, name FROM items LIMIT 1"
[{"id":1,"name":"notebook"}]
$ sqlite3 shop.db ".tables" ".schema items"
CRUD・データ型・STRICT・UPSERT・RETURNING
CRUD の基本
CREATE TABLE members (
id INTEGER PRIMARY KEY, -- 自動採番される rowid の別名
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
point INTEGER NOT NULL DEFAULT 0
);
INSERT INTO members (email, name) VALUES ('a@example.com', 'Aoki'), ('b@example.com', 'Baba');
UPDATE members SET point = point + 100 WHERE email = 'a@example.com';
DELETE FROM members WHERE email = 'b@example.com';
SELECT * FROM members; -- 1|a@example.com|Aoki|100
データ型と「型親和性」を typeof() で確認する
SQLite の列の型は「親和性(affinity)」で、値そのものは INTEGER / REAL / TEXT / BLOB / NULL の 5 種類のどれかとして保存されます。 列に宣言した型は「できればこの型に変換する」というヒントで、変換できない値はそのまま入ります。
CREATE TABLE t_affinity (a INTEGER, b TEXT, c REAL, d BLOB, e NUMERIC, f);
INSERT INTO t_affinity VALUES ('123', 456, '7.5', 'abc', '12.0', 'x');
SELECT typeof(a) AS a, typeof(b) AS b, typeof(c) AS c, typeof(d) AS d, typeof(e) AS e, typeof(f) AS f FROM t_affinity;
┌─────────┬──────┬──────┬──────┬─────────┬──────┐
│ a │ b │ c │ d │ e │ f │
├─────────┼──────┼──────┼──────┼─────────┼──────┤
│ integer │ text │ real │ text │ integer │ text │
└─────────┴──────┴──────┴──────┴─────────┴──────┘
'123' は INTEGER 列で数値に、456 は TEXT 列で文字列に変換されました。一方、INTEGER 列に 'hello' を入れると 変換できないので text のまま格納されます(typeof(a) が text になることを実行で確認)。VARCHAR(255) や DATETIME と書いても親和性に丸められます(TEXT、NUMERIC)。日付・時刻型はなく、TEXT(ISO 8601)か INTEGER(UNIX 時刻)で保存し、datetime() などの関数で扱います。 CURRENT_TIMESTAMP と datetime('now') は UTC を返すので、日本時間が欲しいときは datetime('now', 'localtime') を使います。
STRICT テーブルで型を厳密にする(3.37.0 以降)
「INTEGER 列に文字列が入る」のを防ぎたいときは STRICT を付けます。使える型名は INT / INTEGER / REAL / TEXT / BLOB / ANY の 6 つだけです。
CREATE TABLE t_strict (id INTEGER PRIMARY KEY, qty INTEGER NOT NULL) STRICT;
INSERT INTO t_strict (qty) VALUES ('10'); -- 数値に変換できる文字列は OK
INSERT INTO t_strict (qty) VALUES ('ten'); -- エラー
-- Runtime error: cannot store TEXT value in INTEGER column t_strict.qty (19)
VARCHAR(10) のような型名を STRICT テーブルに書くと Parse error: unknown datatype になります。
アプリから型のそろったデータを入れるテーブルは STRICT にしておくと、後工程の型変換で悩まなくなります。
AUTOINCREMENT は原則不要
INTEGER PRIMARY KEY の列は rowid の別名になり、何もしなくても自動採番されます。AUTOINCREMENT を付けた表と付けない表に a, b を入れて id=2 を削除し、c を追加すると次の違いが出ます(実行で確認)。
| 選択 | c に付く id | 挙動 | 向いている場面 |
|---|---|---|---|
INTEGER PRIMARY KEY |
2 | 最大値 + 1 を採番。末尾を削除すると番号が再利用されることがある | ほとんどの用途(公式も既定で推奨) |
INTEGER PRIMARY KEY AUTOINCREMENT |
3 | 過去に使った番号を再利用しない。sqlite_sequence の更新が毎回発生 |
id を外部に公開し、再利用されると困る場合 |
UPSERT と RETURNING
UPSERT(3.24.0 以降)は「あれば更新、なければ追加」を 1 文で書く構文で、excluded.列名 で「入れようとした値」を参照します。RETURNING(3.35.0 以降)は変更した行を返すので、採番された id をすぐ受け取れます。
INSERT INTO members (email, name) VALUES ('c@example.com', 'Chiba') RETURNING id, email;
-- 2|c@example.com
INSERT INTO members (email, name, point) VALUES ('a@example.com', 'Aoki Taro', 10)
ON CONFLICT(email) DO UPDATE SET name = excluded.name, point = point + excluded.point
RETURNING *;
-- 1|a@example.com|Aoki Taro|110
INSERT INTO members (email, name) VALUES ('a@example.com', 'dup');
-- Runtime error: UNIQUE constraint failed: members.email (19)
ON CONFLICT(email) の列には UNIQUE 制約か主キーが必要です。重複を黙って飛ばしたいだけなら INSERT OR IGNORE が使えます。RETURNING の行の順序は保証されないため、順序が必要なら別途 SELECT します。
JSON 関数(json_extract / -> / ->> / json_each)
3.38.0 以降は JSON 関数が既定で組み込まれ、-> と ->> 演算子も使えます。 JSON は TEXT 列にそのまま保存します(3.45.0 からはバイナリ形式 jsonb も選べます)。
CREATE TABLE orders (id INTEGER PRIMARY KEY, detail TEXT);
INSERT INTO orders (detail) VALUES
('{"customer":"Aoki","items":[{"sku":"A1","qty":2},{"sku":"B2","qty":1}],"paid":true}'),
('{"customer":"Baba","items":[{"sku":"C3","qty":5}],"paid":false}');
SELECT id, json_extract(detail, '$.items[0].sku') AS first_sku,
json_array_length(detail, '$.items') AS n_items,
detail -> '$.customer' AS arrow, detail ->> '$.customer' AS double_arrow FROM orders;
┌────┬───────────┬─────────┬────────┬──────────────┐
│ id │ first_sku │ n_items │ arrow │ double_arrow │
├────┼───────────┼─────────┼────────┼──────────────┤
│ 1 │ A1 │ 2 │ "Aoki" │ Aoki │ ← -> は JSON のまま、->> は SQL の値
│ 2 │ C3 │ 1 │ "Baba" │ Baba │
└────┴───────────┴─────────┴────────┴──────────────┘
| 演算子・関数 | 返るもの | 使いどころ |
|---|---|---|
col -> '$.key' |
JSON テキスト(文字列なら "..." つき) |
ネストした部分を JSON のまま取り出す |
col ->> '$.key' |
SQL の値(text / integer / real / null) | WHERE 条件、表示、集計 |
json_each(col, '$.items') |
配列の各要素を行として返すテーブル値関数 | 配列の展開・集計 |
json_set / json_insert / json_replace / json_remove |
更新後の JSON | 一部だけ書き換える |
配列を行に展開して集計する例です。true / false は ->> で取り出すと 1 / 0(integer)になります。
SELECT o.id, j.value ->> '$.sku' AS sku, j.value ->> '$.qty' AS qty
FROM orders o, json_each(o.detail, '$.items') j;
-- 1|A1|2 / 1|B2|1 / 2|C3|5
SELECT o.id, sum(j.value ->> '$.qty') AS total_qty
FROM orders o, json_each(o.detail, '$.items') j GROUP BY o.id;
-- 1|3 / 2|5
CREATE INDEX idx_orders_customer ON orders(detail ->> '$.customer'); -- 式インデックス
EXPLAIN QUERY PLAN SELECT * FROM orders WHERE detail ->> '$.customer' = 'Aoki';
-- `--SEARCH orders USING INDEX idx_orders_customer (<expr>=?)
JSON のキーで検索するなら ->> の式インデックスを作ると、実行計画が SCAN から SEARCH に変わります。 一部だけ書き換えるには json_set(detail, '$.paid', json('true')) のように使い、真偽値は json('true') と書きます(文字列 'true' と区別するため)。
WAL モードと同時アクセス(database is locked の再現と対処)
SQLite は「同時に書き込めるのは 1 接続だけ」です。 既定のロールバックジャーナル(journal_mode=DELETE)では、読み取りトランザクションが開いている間は書き込みの COMMIT も待たされます。WAL(Write-Ahead Logging)モードにすると、読み取りが書き込みをブロックしなくなります。 次の Python スクリプトは timeout=0(待たずに即エラー)で両モードの違いを再現したものです。
import sqlite3
def demo(journal_mode): # 事前に tasks 表だけの lock_XXX.db を作って journal_mode を設定しておく
path = f"lock_{journal_mode}.db"
print(f"=== journal_mode={journal_mode} ===")
# isolation_level=None: 自動コミット。timeout=0: ロック待ちをせず即エラー
writer, reader, other = (sqlite3.connect(path, timeout=0, isolation_level=None) for _ in range(3))
reader.execute("BEGIN"); reader.execute("SELECT count(*) FROM tasks").fetchone() # 読み取り中のまま
writer.execute("BEGIN"); writer.execute("INSERT INTO tasks (title) VALUES ('a')")
try:
writer.execute("COMMIT"); print("writer: COMMIT OK(読み取り中でも書き込める)")
except sqlite3.OperationalError as e:
print("writer: COMMIT ->", e)
reader.execute("COMMIT"); writer.execute("COMMIT"); print("writer: 読み取り側を閉じたら COMMIT OK")
writer.execute("BEGIN IMMEDIATE") # 書き込みロックを先に取る
try:
other.execute("BEGIN IMMEDIATE")
except sqlite3.OperationalError as e:
print("other : BEGIN IMMEDIATE ->", e)
writer.execute("COMMIT")
for c in (writer, reader, other): c.close()
demo("DELETE"); demo("WAL")
=== journal_mode=DELETE ===
writer: COMMIT -> database is locked
writer: 読み取り側を閉じたら COMMIT OK
other : BEGIN IMMEDIATE -> database is locked
=== journal_mode=WAL ===
writer: COMMIT OK(読み取り中でも書き込める)
other : BEGIN IMMEDIATE -> database is locked
| 状況 | DELETE(既定) | WAL |
|---|---|---|
| 読み取り中に別接続が書き込み COMMIT | database is locked |
OK |
| 書き込み中に別接続が読み取り | OK(コミットの瞬間だけ待つ) | OK |
| 書き込み中に別接続が書き込み | database is locked |
database is locked(書き込みは常に 1 つ) |
database is locked への対処は次の 3 つの組み合わせです。
- WAL モードにする:
PRAGMA journal_mode = WAL;を一度実行すれば DB ファイルに記録され、以後の接続でも有効です。接続中は*.db-walと*.db-shmが横に作られ、最後の接続が閉じるときに本体へ反映(チェックポイント)されます。ネットワーク共有ドライブ上のファイルでは使えません。 - 待ち時間を設定する:
PRAGMA busy_timeout = 5000;(ミリ秒)。CLI の既定は 0、Python のsqlite3.connect()は既定で 5 秒待ちます。 - 書き込みトランザクションは短く、
BEGIN IMMEDIATEで始める:先にロックを取り、途中でdatabase is lockedになる事態を減らします。
CSV のインポート/エクスポート
.import --csv は取り込み先テーブルがなければ 1 行目を列名として自動作成しますが、その場合は全列が TEXT になります(.schema で確認すると "id" TEXT, "price" TEXT, ... になります)。型を効かせたいときは先に CREATE TABLE し、--skip 1 でヘッダー行を飛ばします。
$ cat items.csv
id,name,price,stock
1,notebook,120,50
2,pen,100,200
3,sticky,250,30
$ sqlite3 imp.db "CREATE TABLE items2(id INTEGER PRIMARY KEY, name TEXT, price INTEGER, stock INTEGER);" \
".import --csv --skip 1 items.csv items2" \
"SELECT id, typeof(id), price, typeof(price) FROM items2 LIMIT 1"
1|integer|120|integer
$ sqlite3 -csv -header shop.db "SELECT id, name, price FROM items" > out.csv # エクスポート
| つまずき | 起きること | 対処 |
|---|---|---|
--skip 1 を付けずに既存テーブルへ取り込む |
ヘッダー行がデータとして入る | --skip 1 を付ける |
| テーブル自動作成に任せる | 全列 TEXT、ORDER BY price が文字列順になる |
先に型つきで CREATE TABLE |
| 文字コードが Shift_JIS | 文字化け | 事前に UTF-8 へ変換(iconv -f SHIFT_JIS -t UTF-8) |
Python の sqlite3 モジュール(コピペ例と実行結果)
Python 3.11 の標準モジュール sqlite3 だけで、接続・パラメータ付き実行・一括挿入・トランザクションまで扱えます。 以下は 1 本のスクリプトで、そのまま実行できます。
import sqlite3
con = sqlite3.connect("app.db") # ファイルがなければ作られる
con.row_factory = sqlite3.Row # 行を列名でアクセスできるようにする
con.executescript("""
CREATE TABLE IF NOT EXISTS members (
id INTEGER PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY,
member_id INTEGER NOT NULL REFERENCES members(id),
title TEXT NOT NULL,
done INTEGER NOT NULL DEFAULT 0
);
""")
# 1) パラメータは ? または :name(文字列連結はしない)
with con: # with: 正常終了で commit、例外で rollback
con.execute("INSERT INTO members (email, name) VALUES (?, ?)", ("a@example.com", "Aoki"))
con.execute("INSERT INTO members (email, name) VALUES (:email, :name)",
{"email": "b@example.com", "name": "Baba"})
# 2) executemany でまとめて INSERT
rows = [(1, "write report"), (1, "review PR"), (2, "update docs")]
with con:
con.executemany("INSERT INTO tasks (member_id, title) VALUES (?, ?)", rows)
# 3) 例外が起きると with ブロック内の変更はロールバックされる
try:
with con:
con.execute("INSERT INTO tasks (member_id, title) VALUES (?, ?)", (2, "rolled back"))
con.execute("INSERT INTO members (email, name) VALUES (?, ?)", ("a@example.com", "dup"))
except sqlite3.IntegrityError as e:
print("IntegrityError:", e)
print("tasks count:", con.execute("SELECT count(*) FROM tasks").fetchone()[0]) # 3 のまま
# 4) Row で列名アクセス
for row in con.execute("""
SELECT m.name, t.title, t.done
FROM tasks t JOIN members m ON m.id = t.member_id ORDER BY t.id"""):
print(dict(row))
# 5) RETURNING で採番された id を受け取る
with con:
new_id = con.execute("INSERT INTO tasks (member_id, title) VALUES (?, ?) RETURNING id",
(1, "deploy")).fetchone()["id"]
con.execute("UPDATE tasks SET done = 1 WHERE id = ?", (new_id,))
print("new task id:", new_id)
con.close()
# 6) インメモリ DB(テストに便利)
mem = sqlite3.connect(":memory:")
mem.execute("CREATE TABLE t (v)")
mem.executemany("INSERT INTO t VALUES (?)", [(i,) for i in range(5)])
print("memory sum:", mem.execute("SELECT sum(v) FROM t").fetchone()[0])
$ python3 py_demo.py
IntegrityError: UNIQUE constraint failed: members.email
tasks count: 3
{'name': 'Aoki', 'title': 'write report', 'done': 0}
{'name': 'Aoki', 'title': 'review PR', 'done': 0}
{'name': 'Baba', 'title': 'update docs', 'done': 0}
new task id: 4
memory sum: 10
with con: は commit / rollback だけを行い接続は閉じないので、最後に con.close() します。外部キー制約は既定では効かないため、使うなら接続ごとに con.execute("PRAGMA foreign_keys = ON") を実行します(違反時は IntegrityError: FOREIGN KEY constraint failed)。Python 3.12 以降は sqlite3.connect(..., autocommit=False) でトランザクション制御を明示でき、sqlite3.version は 3.14 で削除されたので、ライブラリのバージョンは sqlite3.sqlite_version で確認します。
1 行ずつコミットせず、トランザクションでまとめる(実測)
SQLite は コミットのたびにディスクへ同期するため、1 行ずつ自動コミットすると非常に遅くなります。20,000 行の INSERT を、自動コミット接続(isolation_level=None)で「1 行ずつ execute」と「BEGIN → executemany → COMMIT」で比べた結果です。
1行ずつ自動コミット : 19.54 秒 (20000 行)
BEGIN〜COMMIT でまとめて: 0.02 秒 (20000 行)
大量の INSERT / UPDATE は必ず 1 つのトランザクションにまとめます(with con: の中で executemany を使えば自然にそうなります)。実測値は環境によって変わりますが、桁が違う差になるのは共通です。
バックアップ(.backup / VACUUM INTO / Python の backup)
稼働中の DB ファイルを cp でコピーすると、書き込みの途中を写してしまうことがあります。 一貫したバックアップは次のいずれかで取ります(いずれも実行して PRAGMA integrity_check; が ok になることを確認済み)。
| 方法 | コマンド | 特徴 |
|---|---|---|
| オンラインバックアップ | sqlite3 app.db ".backup app_copy.db" |
稼働中でも一貫した複製。復元は .restore |
| VACUUM INTO(3.27.0 以降) | sqlite3 app.db "VACUUM INTO 'app_vacuum.db';" |
削除済み領域を詰めた最小サイズの複製。元 DB は変更しない |
| SQL ダンプ | sqlite3 app.db ".dump" > app.sql |
テキスト。別バージョン・別 DB への移行にも。復元は sqlite3 new.db < app.sql |
| Python | with dst: src.backup(dst) |
アプリ内から定期バックアップ(Python 3.7 以降) |
WAL モードの DB をファイルコピーで退避するときは *.db-wal も一緒に、あるいは PRAGMA wal_checkpoint(TRUNCATE); を実行してからにします。
PostgreSQL / MySQL との使い分け
SQLite は「同時書き込みが少なく、1 台の中で完結する」用途に向いています。 複数のサーバーから同時に書く、ユーザー権限を分ける、といった要件が出てきたらサーバー型 DB を選びます。
| 観点 | SQLite | PostgreSQL / MySQL |
|---|---|---|
| 構成 | ライブラリ+ファイル 1 つ。サーバー・ポート・ユーザー管理なし | サーバープロセス。ネットワーク越しに接続 |
| 同時書き込み | 常に 1 接続(WAL でも同じ)。読み取りは WAL で並行可 | 多数の接続が行単位で同時に書ける |
| ネットワーク共有 | 共有ドライブや複数ホストからの同時アクセスは非対応 | 前提として対応 |
| 型 | 親和性(緩い)。STRICT で厳密化 | 厳密な型 |
| ALTER TABLE | RENAME / ADD COLUMN / RENAME COLUMN / DROP COLUMN のみ。型変更や制約追加はテーブル作り直し | 列の型変更・制約追加が可能 |
| ユーザー・権限 | なし(OS のファイル権限で管理) | ロールと GRANT |
| 適した用途 | アプリ内蔵 DB、CLI ツール、テスト、ログ・キャッシュ、読み取り中心の小〜中規模サイト | 複数サービスから書き込む業務システム、大規模 Web |
| バックアップ | ファイル複製(.backup / VACUUM INTO) |
pg_dump / mysqldump、レプリケーション |
列の型変更や制約の追加は、新しい定義でテーブルを作り INSERT INTO new SELECT ... FROM old でコピーし、古い表を DROP → RENAME する手順(公式ドキュメントの 12 ステップ)で行います(ALTER TABLE t ALTER COLUMN ... は syntax error になります)。
よくある質問
Q. sqlite3 コマンドが見つかりません。Python では使えているのに、なぜですか?
A. Python の sqlite3 モジュールはライブラリを同梱しているため、CLI(sqlite3 コマンド)とは別物です。CLI は apt-get install sqlite3 などで別途入れます。sqlite3.sqlite_version を見ると、Python 同梱ライブラリのバージョンがわかります。
Q. database is locked が出たとき、まず何を確認すればよいですか?
A. (1) 同時に書き込む処理が複数ないか、(2) 読み取りトランザクションを開いたままにしていないか、(3) journal_mode が WAL か、(4) busy_timeout が 0 になっていないか、の順で確認します。本文の再現スクリプトの通り、WAL にすると読み取りと書き込みの競合はなくなります。
Q. 日付はどう保存すればよいですか?
A. 専用の型はないので、TEXT に ISO 8601(2026-09-30 12:34:56)で保存するのが扱いやすいです。datetime('now') は UTC を返すため、日本時間なら datetime('now', 'localtime') を使います。文字列のままでも比較や並べ替えは正しく動きます。
まとめ
- SQLite はサーバー不要・ファイル 1 つの DBで、
sqlite3 ファイル名で即使える。公式最新は 3.53.4、apt 版は 3.45.1 - ドットコマンドは
.tables.schema.mode box.headers on.import.dump.backupを押さえれば日常の操作は足りる - 型は「親和性」で緩い。厳密にしたいテーブルは
STRICT、id はINTEGER PRIMARY KEYだけで自動採番される - UPSERT と RETURNING で「あれば更新、なければ追加して id を受け取る」が 1 文で書ける
- JSON は
->>とjson_eachで取り出し・展開でき、式インデックスで検索も速くなる - 書き込みは常に 1 接続。WAL モード+busy_timeout+短いトランザクションで
database is lockedを避ける - Python は標準の
sqlite3で完結し、with con:とプレースホルダ、executemanyのまとめ書きが基本
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「SQLiteの使い方|コマンド早見表とPython連携を実行例で解説」
株式会社RJCでは、SQLite のような小さく確実に動く仕組みから業務システムのデータベース設計までを、プロジェクトの中で仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。