この記事について
この記事は、生成AIを活用した自動生成フローで作成しています。SQLite公式のPRAGMA仕様を参照し、quick_check、integrity_check、foreign_key_checkの違いを安全なインメモリDBのサンプルで解説します。サンプルの実機実行は未確認です。
検証ステータス:📘 SQLite公式資料確認済み/実機未確認
SQLiteのDBが正常か確かめるために、PRAGMA integrity_checkを使うことがあります。出力がokなら安心でしょうか。答えは「構造チェックとしてはよい結果だが、外部キーを含む業務データの正しさまでは保証しない」です。
SQLiteにはquick_checkとintegrity_check、そして別のforeign_key_checkがあります。外部キー違反のあるデータをわざと作り、3つの違いを比較します。
3種類の検査
| 機能 | 確認する主な対象 | 注意点 |
|---|---|---|
PRAGMA quick_check | 低レベルの簡易検査 | UNIQUEと索引/表の一致などを一部省略 |
PRAGMA integrity_check | 詳細な低レベル整合性と制約 | 外部キー違反は対象外 |
PRAGMA foreign_key_check | 参照先のない子レコード | ファイル破損を網羅するものではない |
SQLiteのPRAGMA公式資料は、integrity_checkが外部キー違反を検出せず、foreign_key_checkを使う必要があると説明しています。
何も壊さずに試す
次のコードはファイルを使わず、:memory:という揮発性DBを用意します。Python標準ライブラリのsqlite3以外は不要です。
import sqlite3
with sqlite3.connect(":memory:") as db:
# 教材用の接続内だけFK強制をOFFにする
db.execute("PRAGMA foreign_keys=OFF")
db.executescript("""
CREATE TABLE parent (id INTEGER PRIMARY KEY);
CREATE TABLE child (
id INTEGER PRIMARY KEY,
parent_id INTEGER REFERENCES parent(id)
);
INSERT INTO parent(id) VALUES (1);
INSERT INTO child(id, parent_id) VALUES (10, 99);
""")
db.commit()
db.execute("PRAGMA foreign_keys=ON")
for label, statement in [
("quick_check", "PRAGMA quick_check"),
("integrity_check", "PRAGMA integrity_check"),
("foreign_key_check", "PRAGMA foreign_key_check")
]:
print(label, db.execute(statement).fetchall())
既存のDBに触れずにテストできます。次のコマンドで実行します。
python3 check-db.py
ファイル全体はDaily-Code-Samples:SQLiteチェックの比較に保存しました。
期待される出力
quick_check [('ok',)]
integrity_check [('ok',)]
foreign_key_check [('child', 10, 'parent', 0)]
これは実行結果の予想例であり、実機の転記ではありません。親テーブルにはID=1だけがあり、子テーブルには存在しない親ID=99を参照するレコードがあるため、最後のチェックだけで違反が見つかる想定です。
DBが読み取れる・構造が整っている・データの意味が正しいはそれぞれ別の条件です。
1か所変更して違いを見る
サンプル中のこの行を変えてください。
-- 変更前 INSERT INTO child(id, parent_id) VALUES (10, 99); -- 変更後 INSERT INTO child(id, parent_id) VALUES (10, 1);
存在する親を参照するため、変更後はforeign_key_checkでも違反が返らなくなるはずです。両方の構造検査はokのままと予想できます。
もう一つの観察点はPRAGMA foreign_keys=ONの位置です。既にトランザクションが開始されている間に切り替えても、期待どおり反映されない場合があります。今回commit()を明示するのはそのためです。
なぜ検査を分けるのか
SQLiteのインデックスと行データの整合性確認にはファイル構造を調べる処理が必要です。一方、外部キーの参照関係はテーブル内の値同士の関係です。
例として、顧客マスターに存在しない顧客IDを受注明細が参照していても、SQLiteファイルが破損しているとは限りません。内部構造の検査が成功したからといって、売上集計や請求の整合性まで保証されません。
逆に、業務データの参照関係が正しく見えても、ディスク障害がない保証にもなりません。役割の違う検査を混同しないことが重要です。
本番DBで気を付けること
このデモではFK制約の強制をOFFにして違反行を作成していますが、業務DBでは真似しないでください。既存のデータベースを診断するために制約を無効にする必要はありません。
実際のDBを対象にするときは、データ量に応じた検査時間、バックアップ、WALモード、他プロセスによる同時更新、権限を先に検討します。読取り操作でもディスクI/Oやロックの負荷は発生し得ます。
検査でエラーが出ても、原因が分からないままファイルを削除したりREINDEXを実施したりせず、バックアップを保全して原因を調査します。
運用での使い分け
日常の軽い観測:quick_checkが候補。ただし省略する検査項目を理解する。
不整合が疑われるとき:integrity_checkの詳細結果を確認する。
外部キーの整合性:foreign_key_checkを別に行う。
それでも「業務値が正しい」とは限らないので、アプリケーションのチェックと突き合わせる。
