.NET C# で SQLite を使ってみる

Yossy
2026-02-18
2026-02-18

.NET C#プロジェクトで大量のデータ扱う時に、気軽にSQLでデータを操作したいときに、**SQLite** は、選択肢として適してるかと思い、基本的な操作について調べてみたので、以下まとめてみます。

1. SQLite を採用するメリット

SQLite はサーバーレスで動作するリレーショナルデータベースで、以下の特性を持ちます。

運用の簡略化
 DBサーバーの構築が不要。データベース全体が単一のファイル(`.db`)として完結。
ポータビリティ
 実行バイナリとDBファイルを配布するだけで動作し、環境依存が極めて少ない。
扱いやすさ
  標準的なSQLをサポートしており、将来的に他のRDBMS(MySQL, PostgreSQL等)へ移行する際の敷居も低い。

2. 開発環境のセットアップ

.NET環境では、公式ライブラリである `Microsoft.Data.Sqlite` が利用できます。

3. 基本実装:接続とスキーマ定義

データベースへの接続には `SqliteConnection` を使用します。

using Microsoft.Data.Sqlite;

// 接続文字列の定義
var connectionString = "Data Source=sample.db";

using (var connection = new SqliteConnection(connectionString))
{
    connection.Open();

    // テーブル定義の実行
    var createTableSql = @"
        CREATE TABLE IF NOT EXISTS Users (
            Id INTEGER PRIMARY KEY AUTOINCREMENT,
            Name TEXT NOT NULL,
            Age INTEGER
        )";

    using var command = new SqliteCommand(createTableSql, connection);
    command.ExecuteNonQuery();
}
`using` ブロックを抜ける際に `Dispose()` が呼ばれ、接続が確実に閉じられるようにしています。


4. データ操作:安全性とパラメータ利用

SQLインジェクション脆弱性を排除するため、文字列結合を避け、**パラメータ(プレースホルダー)**を利用します。

var insertSql = "INSERT INTO Users (Name, Age) VALUES ($name, $age)";
using var command = new SqliteCommand(insertSql, connection);

// パラメータによる安全な値のセット
command.Parameters.AddWithValue("$name", "田中太郎");
command.Parameters.AddWithValue("$age", 25);

command.ExecuteNonQuery();

5. データの読み出し (SELECT)

データの検索や取得には `ExecuteReader` を使用します。実行結果として返される `SqliteDataReader` を反復処理することで、各行のデータにアクセスできます。

var selectSql = "SELECT Id, Name, Age FROM Users WHERE Age >= $age";
using var command = new SqliteCommand(selectSql, connection);
command.Parameters.AddWithValue("$age", 20);

using (var reader = command.ExecuteReader())
{
    while (reader.Read())
    {
        // 序数(インデックス)指定による型安全なデータ取得
        var id = reader.GetInt32(0);
        var name = reader.GetString(1);
        var age = reader.GetInt32(2);

        Debug.WriteLine($"ID: {id}, Name: {name}, Age: {age}");
    }
}

ExecuteReader
複数行の結果セットを取得する際に使用。
Read() メソッド
次の行が存在する場合は `true` を返し、カーソルを一行進める。
型安全な取得
`GetInt32` や `GetString` メソッドを利用することで、キャストによるランタイムエラーのリスクを低減できます。


6. パフォーマンス最適化:トランザクションの活用

SQLite はデフォルトで「自動コミットモード」になっており、1命令ごとにディスクI/Oが発生します。なので、大量のデータを逐次挿入すると著しく低速になります。数千件規模のデータを扱う場合は、**トランザクション**を用いて書き込みをバッチ化することで、劇的な高速化が可能です。


// トランザクションの明示的な開始
using var transaction = connection.BeginTransaction();

try 
{
    var command = connection.CreateCommand();
    command.Transaction = transaction;
    command.CommandText = "INSERT INTO Users (Name, Age) VALUES ($name, $age)";

    for (int i = 0; i < 10000; i++) 
    {
        command.Parameters.Clear();
        command.Parameters.AddWithValue("$name", $"User_{i}");
        command.Parameters.AddWithValue("$age", 20);
        command.ExecuteNonQuery();
    }

    // 全ての処理が成功した場合に確定
    transaction.Commit();

catch (Exception) 
{
    // エラー発生時はロールバックを行い、データのアトミック性を担保
    transaction.Rollback();
    throw;
}





結論と今後の展望

SQLiteの導入障壁は想像以上に低く、その軽量さと柔軟性は特筆に値します。これまでインメモリ(Dictionary型とLINQの組み合わせ)で管理していたキャッシュ情報などを、使い慣れたSQLによる操作へと置き換えられる点は、設計上の大きなアドバンテージです。

今後は、データの複雑化に備え、インデックス設計によるさらなる高速化や、マルチスレッド環境下での適切な同時実行制御(WALモードの活用など)について検討を深めていく予定です。