Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

SQLite3 のメンテナンスを C# アプリケーションの起動時に実施する

0
Posted at

はじめに

SQLite3 のメンテナンスを C# アプリケーションの起動時に実施する備忘録です。

改訂履歴

  • 2026/08/14 : 初版公開。

本文

1. 環境

  • .NET 8.0
  • Microsoft.Data.Sqlite.Core 10.0.11
  • SQLite3 3.53.3
  • SQLitePCLRaw.bundle_e_sqlite3 2.1.12
  • Visual Studio Community 2022

2. 実装

SQLiteManager クラスに Backup()MaintenanceAsync(bool isSimple) を実装しました。Backup() の方は Microsoft.Data.Sqlite.SqliteConnectionasync でバックアップできるメソッドがなかったので、同期処理になっています。

SQLiteManager.cs
using System.IO;
using Microsoft.Data.Sqlite;

namespace SQLiteMaintenanceExample;

/// <summary>
/// SQLite データベースの管理を行います。
/// </summary>
public static class SQLiteManager
{
    private static readonly string s_connectionString = "Data Source=DataBase.sqlite";

    /// <summary>
    /// バックアップを行います。
    /// </summary>
    public static void Backup()
    {
        try
        {
            // exe と同じ場所に backup フォルダを作成する。
            var backupDirectory = Path.Combine(AppContext.BaseDirectory, "backup");
            Directory.CreateDirectory(backupDirectory);

            // バックアップファイル名を作成する。
            var fileName = $"DataBase_{DateTime.Now:yyyyMMdd_HHmmss}.sqlite";
            var backupPath = Path.Combine(backupDirectory, fileName);

            // 接続する。
            using var source = new SqliteConnection(s_connectionString);
            source.Open();

            // バックアップ先の接続文字列を作成する。
            var bkConnStringBuilder = new SqliteConnectionStringBuilder
            {
                DataSource = backupPath
            };

            // バックアップを行う。
            using var destination = new SqliteConnection(bkConnStringBuilder.ToString());
            destination.Open();
            source.BackupDatabase(destination);
        }
        catch
        {
            // 異常処理を適宜実装する。
            throw;
        }
    }

    /// <summary>
    /// メンテナンスを非同期で行います。
    /// </summary>
    /// <param name="isSimple">簡易メンテナンスの場合 <see langword="true"/>、それ以外の場合は <see langword="false"/>。</param>
    public static async Task MaintenanceAsync(bool isSimple)
    {
        try
        {
            await using var connection = new SqliteConnection(s_connectionString);
            await connection.OpenAsync();

            // DB 破損チェック。
            await using (SqliteCommand cmd = connection.CreateCommand())
            {
                cmd.CommandText = isSimple
                    ? "PRAGMA quick_check;"
                    : "PRAGMA integrity_check;";

                var result = (await cmd.ExecuteScalarAsync())?.ToString();
                if (!string.Equals(result, "ok", StringComparison.Ordinal))
                {
                    // 異常処理を適宜実装する。
                    throw new Exception("An error occurred.");
                }
            }

            // インデックスの再構築。
            if (!isSimple)
            {
                await using SqliteCommand cmd = connection.CreateCommand();
                cmd.CommandText = "REINDEX;";
                await cmd.ExecuteNonQueryAsync();
            }

            // 統計情報の更新。
            if (!isSimple)
            {
                // PRAGMA optimize を実行するなら ANALYZE は不要なはずだが
                // 念のため、明示的に実行しておく。
                await using SqliteCommand cmd = connection.CreateCommand();
                cmd.CommandText = "ANALYZE;";
                await cmd.ExecuteNonQueryAsync();
            }

            // DB 最適化。
            await using (SqliteCommand cmd = connection.CreateCommand())
            {
                cmd.CommandText = "PRAGMA optimize;";
                await cmd.ExecuteNonQueryAsync();
            }

            // WAL ファイルの整理。
            await using (SqliteCommand cmd = connection.CreateCommand())
            {
                // WAL モードの場合、WAL ファイルを整理して小さくする。
                cmd.CommandText = "PRAGMA journal_mode;";
                var journalMode = (await cmd.ExecuteScalarAsync())?.ToString();
                if (string.Equals(journalMode, "wal", StringComparison.OrdinalIgnoreCase))
                {
                    cmd.CommandText = "PRAGMA wal_checkpoint(TRUNCATE);";
                    await cmd.ExecuteNonQueryAsync();
                }
            }

            // DB 圧縮。
            await using (SqliteCommand cmd = connection.CreateCommand())
            {
                cmd.CommandText = "VACUUM;";
                await cmd.ExecuteNonQueryAsync();
            }
        }
        catch
        {
            // 異常処理を適宜実装する。
            throw;
        }
    }
}

3. 使用例

WPF アプリケーションでの使用例です。

App.xaml.cs
using System.Windows;

namespace SQLiteMaintenanceExample;

/// <summary>
/// Windows Presentation Foundation アプリケーションのエントリーポイントを定義します。
/// </summary>
public partial class App : Application
{
    // 取得元は、適宜変更する。
    private static readonly bool s_isSimpleMaintenance = false;

    /// <summary>
    /// <see cref="Application.Startup"/> イベントを発生させます。
    /// </summary>
    /// <param name="e">イベントデータを含む <see cref="StartupEventArgs"/>。</param>
    protected override async void OnStartup(StartupEventArgs e)
    {
        try
        {
            // データベースのバックアップとメンテナンスを実行する。
            SQLiteManager.Backup();
            await SQLiteManager.MaintenanceAsync(s_isSimpleMaintenance);
        }
        catch (Exception ex)
        {
            // 異常処理を適宜実装する。
            MessageBox.Show(ex.ToString());
        }

        // メインウィンドウを表示する。
        var mainWindow = new MainWindow();
        MainWindow = mainWindow;
        mainWindow.Show();
    }
}

おわりに

DB は、パフォーマンスが低下してからが本番です。

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?