SQLiteのPRAGMA quick_checkとintegrity_checkを比較する ― データベース異常を読み取りで調べる

プログラミング・Web開発カテゴリを表すパンダのイラスト プログラミング・Web開発

この記事について
この記事は、生成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を別に行う。

  • それでも「業務値が正しい」とは限らないので、アプリケーションのチェックと突き合わせる。

公式情報

文書情報

記事タイトル
SQLiteのPRAGMA quick_checkとintegrity_checkを比較する ― データベース異常を読み取りで調べる
作成日
更新日
Source URL
https://papanda925.com/?p=18086

ライセンス: 本記事のうち、当サイトが権利を有する本文・自作図表は、特記なき限り CC BY 4.0 で利用できます。生成AIを活用して作成・編集した内容を含みます。コードについて、別途ライセンス表示またはリンク先GitHubリポジトリのライセンスがある場合は、その条件を優先します。引用・第三者資料・画像・商標等は本ライセンスの対象外です。 利用ポリシー

タイトルとURLをコピーしました