PythonでSQLiteを使う時のメモ

データベースの作成

データベースを新規作成

import sqlite3
conn = sqlite3.connect("example.db")

conn = sqlite3.connect("example.db") は大きく分けて二つのことをしています。

1. データベースファイルの準備

  • 指定したファイル名 (example.db) が存在しない場合は、新しく SQLite のデータベースファイルを作成します。
  • すでに存在していれば、そのファイルを開きます。

2. Connection オブジェクトの作成

  • SQLite データベースとやり取りするための Connection オブジェクト を返します。
  • このオブジェクトを通じて cursor() を生成し、SQL を実行したり、トランザクションを管理したりします。

使用例

import sqlite3

# データベースファイルがなければ作成、あれば接続
conn = sqlite3.connect("example.db")

# SQLを実行するためのカーソルを作成
cur = conn.cursor()

# テーブルを作成
cur.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER, name TEXT)")

# データを挿入
cur.execute("INSERT INTO users VALUES (1, 'Alice')")

# 変更を保存
conn.commit()

# 結果を取得
cur.execute("SELECT * FROM users")
print(cur.fetchall())

# 接続を閉じる
conn.close()

データベースをメモリ上に作る(:memory:)

:memory: は、SQLite に「データベースをディスク上のファイルではなく、メモリ上に作る」という特別な指定です。

sqlite3.connect(":memory:")

この場合、ファイルは作られず、OS が提供する揮発メモリ上に一時的なデータベースが生成されます。

特徴

  • 高速(ディスクI/Oがないため)
  • 揮発性(接続を閉じたりプロセスが終了すると消える)
  • 実験やユニットテスト、一時的な計算に便利

使用例

import sqlite3

conn = sqlite3.connect(":memory:")
cur = conn.cursor()
cur.execute("CREATE TABLE users (id INTEGER, name TEXT)")
cur.execute("INSERT INTO users VALUES (1, 'Alice')")
print(cur.execute("SELECT * FROM users").fetchall())  # [(1, 'Alice')]
conn.close()  # ここでDBは消える

DB-API 2.0(PEP 249)とは

DB-API 2.0 とは、Python でデータベースに統一的にアクセスするための標準インターフェース仕様のことです。正式には PEP 249 (Python Database API Specification v2.0) で定義されています。

背景

Python には多くのデータベース用ライブラリ(SQLite, MySQL, PostgreSQL, Oracle など)が存在します。
それぞれが独自の書き方をしていたらコードがバラバラになってしまうため、共通の規約を定めたのが DB-API 2.0 です。

主な仕様

DB-API 2.0 に準拠したモジュール(例: sqlite3, psycopg2, mysql-connector-python)は、共通のインターフェースを持っています。

  1. 接続 (Connection) conn = module.connect(...) データベースに接続するためのオブジェクトを返す。
  2. カーソル (Cursor) cur = conn.cursor() SQL を実行するためのオブジェクト。
  3. SQL 実行 cur.execute("SELECT * FROM users WHERE id = %s", (1,)) プレースホルダ付きで安全に SQL を実行できる。
  4. 結果取得 rows = cur.fetchall() 複数行・単一行・一部だけの結果取得方法が標準化。
  5. トランザクション管理
    • conn.commit()
    • conn.rollback()
      これも共通のインターフェースで扱える。
  6. 例外クラス
    • DatabaseError
    • OperationalError
      などの例外階層も標準化されているため、ライブラリごとに書き分けなくても済む。

メリット

  • SQLite 用に書いたコードを、PostgreSQL や MySQL 向けに書き換えるときも変更が最小限で済む。
  • Python のデータベースライブラリを学ぶときに「共通の学習曲線」で済む。

まとめ

DB-API 2.0(PEP 249)は、

  • Python 標準のデータベースアクセス仕様
  • Connection / Cursor / execute / fetch / commit などが統一されている
  • SQLite3 モジュールもこの仕様に準拠している

つまり、sqlite3 を使っている時点で、すでに DB-API 2.0 を体験していることになります。

ConnectionとCursorの存在意義と使い方

Connection の存在意義

Connection は、データベースそのものとの「接続」を表すオブジェクトです。
これがあることで、以下のような役割を担います。

  • データベースファイルやサーバーとのセッションを管理する
  • トランザクション(commit, rollback)の制御を行う
  • Cursor を生成するための窓口となる

Python で sqlite3.connect("example.db") と書いたときに返ってくるオブジェクトがこれです。

Cursor の存在意義

Cursor は、データベースに対して実際に SQL を発行するための「作業カーソル」です。
役割は以下の通りです。

  • SQL 文の実行 (execute, executemany)
  • 実行結果の取得 (fetchone, fetchall, fetchmany)
  • クエリ実行の状態を保持する(次の取得位置など)

複数のクエリを同時に扱いたい場合、複数のカーソルを作ることもできます。

使い方の流れ

import sqlite3

# データベースに接続(Connectionの作成)
conn = sqlite3.connect("example.db")

# SQLを実行するためのカーソルを作成
cur = conn.cursor()

# テーブル作成
cur.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER, name TEXT)")

# データ挿入
cur.execute("INSERT INTO users VALUES (?, ?)", (1, "Alice"))

# トランザクションを確定(Connectionの役割)
conn.commit()

# データ取得
cur.execute("SELECT * FROM users")
rows = cur.fetchall()
print(rows)

# 後片付け
cur.close()   # カーソルを閉じる
conn.close()  # コネクションを閉じる

まとめ

  • Connection: データベースとの接続を管理し、トランザクションやカーソル生成の役割を担う
  • Cursor: SQL 実行や結果取得を担当する作業単位
  • 一般的な流れは「Connection を作る → Cursor を作る → SQL を実行する → 結果を取得する → commit/close」で進める

CREATE TABLE の基本構文

CREATE TABLE は、データベースに新しいテーブル(表)を作成するための SQL 文です。基本形は以下です。

CREATE TABLE テーブル名 (
    列名 データ型 制約,
    列名 データ型 制約,
    ...
);

主な要素

  1. テーブル名
    • データを格納する表の名前。
    • 例: users, products など。
  2. 列名
    • 各列の識別子。テーブルのカラムに相当。
  3. データ型
    SQLite の場合は柔軟で、代表的な型は以下の通り。
    • INTEGER(整数)
    • REAL(浮動小数点)
    • TEXT(文字列)
    • BLOB(バイナリデータ)
  4. 制約
    列やテーブルにかけるルール。代表的なもの:
    • PRIMARY KEY : 主キー(行を一意に識別)
    • AUTOINCREMENT : 主キーの自動連番
    • NOT NULL : 空値を許さない
    • UNIQUE : 値の重複を禁止
    • DEFAULT 値 : デフォルト値を指定
    • CHECK (条件) : 条件を満たす値のみ許可
    • FOREIGN KEY : 外部キー制約(他テーブルとの関連)

例1: 基本的なテーブル作成

CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE,
    age INTEGER
);

例2: 外部キーを使ったテーブル

CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    product TEXT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

既に存在している場合にエラーを避ける方法

IF NOT EXISTS を付けると、テーブルが存在する場合にエラーを出さずスキップする。

CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY,
    name TEXT
);

まとめ

  • CREATE TABLE でテーブルの構造を定義する
  • 列名、データ型、制約を組み合わせて設計する
  • 主キーや外部キーを設定してデータの整合性を守る
  • IF NOT EXISTS で安全にテーブルを作成できる

INSERT の基本構文

INSERT 文は、テーブルに新しいデータ(行)を追加するために使います。基本形は以下の通りです。

INSERT INTO テーブル名 (列1, 列2, ...) VALUES (値1, 値2, ...);

主な書き方

  1. 列を指定して挿入
INSERT INTO users (id, name, email, age) VALUES (1, 'Alice', 'alice@example.com', 25);
  1. すべての列に挿入
    列リストを省略すると、テーブル定義にある全列に対応する値を順番通りに指定する必要があります。
INSERT INTO users VALUES (2, 'Bob', 'bob@example.com', 30);
  1. 複数行を一度に挿入
    SQLite では複数行をまとめて挿入できます。
INSERT INTO users (name, email, age) VALUES
('Charlie', 'charlie@example.com', 22),
('Diana', 'diana@example.com', 28);
  1. 一部の列だけに挿入
    指定しなかった列は NULL またはデフォルト値が入ります。
INSERT INTO users (name, email) VALUES ('Eve', 'eve@example.com');

プレースホルダを使う(Python から実行する場合)

Python で sqlite3 を使う場合は SQL インジェクション対策のため、プレースホルダを使うのが基本です。

import sqlite3

conn = sqlite3.connect("example.db")
cur = conn.cursor()

cur.execute("INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
            ("Frank", "frank@example.com", 35))

conn.commit()
conn.close()

注意点

  1. 必要に応じて明示的に conn.commit() を呼ぶ
    • Python の sqlite3 モジュールを使うときは 明示的に commit するまで変更が確定されません。データを永続化したいときは必ず呼ぶ
  2. 一時的なデータ(テストなど)なら commit せず終了しても良い
  3. 自動コミットモードを有効化する
    接続時に以下のように isolation_level=None を指定すると、自動的にコミットされます。
    conn = sqlite3.connect("example.db", isolation_level=None)
    ただし、トランザクションを細かく制御できなくなるので注意が必要です。

まとめ

  • INSERT INTO ... VALUES ... で新しい行を追加する
  • 列を明示的に指定すると安全かつ可読性が高い
  • 複数行をまとめて挿入できる
  • Python などから使う場合はプレースホルダで安全に値を渡す
  • INSERT しても commit() しない限り変更は保存されない

DML, DDL, DCL, DQLとは

DML (Data Manipulation Language)

データ操作言語。テーブルに格納されたデータを操作するための文。

  • 代表例: INSERT, UPDATE, DELETE
  • 目的: データの追加・更新・削除などを行う
  • 注意点: トランザクション管理が必要。commitrollback で確定や取り消しを行う

DDL (Data Definition Language)

データ定義言語。データベースの構造そのものを定義・変更する文。

  • 代表例: CREATE, ALTER, DROP, TRUNCATE
  • 目的: テーブルやビュー、スキーマなどの構造を作成・変更・削除する
  • 特徴: 多くのデータベースでは実行すると即時に反映され、rollback できない場合が多い

DCL (Data Control Language)

データ制御言語。データベースに対する権限や制御を扱う文です。

  • 代表例: GRANT, REVOKE
  • 目的: ユーザーやロールに対するアクセス権限を付与・剥奪する
  • 特徴: セキュリティや管理に関わるため、管理者権限で使用されることが多い

DQL (Data Query Language)

データ検索言語。データの検索を行う文です。

  • 代表例: SELECT
  • 目的: テーブルから条件に合ったデータを取り出す
  • 特徴: データの読み取りに特化しており、テーブルの内容を変更しない

fetchのやり方と仕組み

fetch の役割

fetch 系メソッドは、SQL の実行結果から実際にデータを Python オブジェクトとして取り出すための仕組みです。SELECT 文を実行すると、カーソルが結果セット(行の集合)を保持します。その結果セットからデータを取り出すのが fetch です。

代表的なメソッド

  1. fetchone()
  • 結果セットから次の1行を返す
  • 返す行がなければ None を返す
cur.execute("SELECT * FROM users")
row = cur.fetchone()
print(row)  # (1, 'Alice', 'alice@example.com', 25)
  1. fetchmany(size)
  • 指定した件数の行をまとめて取得
  • 結果が残り少ない場合はそれ以下の行数になる
cur.execute("SELECT * FROM users")
rows = cur.fetchmany(2)
print(rows)  # [(1, 'Alice', ...), (2, 'Bob', ...)]
  1. fetchall()
  • 結果セットの残りすべてを取得してリストで返す
  • 件数が多いとメモリを大量に消費するので注意
cur.execute("SELECT * FROM users")
rows = cur.fetchall()
print(rows)  # [(1, 'Alice', ...), (2, 'Bob', ...), ...]

仕組み

  • cursor.execute() を呼ぶと、SQL の結果がカーソルに「内部バッファ」として保持される
  • fetchone() はカーソルの現在位置から1行だけ取り出し、カーソルを次の行へ進める
  • fetchmany() はカーソルを size 分だけ進める
  • fetchall() は残り全部を一度にリスト化する
  • そのため fetchone() を繰り返して全件取得することも可能

使い分けのポイント

  • 1件だけ取得したい場合: fetchone()
  • 少量ずつ処理したい場合: fetchmany(size) をループ
  • 全件まとめて処理しても問題ない場合: fetchall()

実践例

import sqlite3

conn = sqlite3.connect(":memory:")
cur = conn.cursor()
cur.execute("CREATE TABLE users (id INTEGER, name TEXT)")
cur.executemany("INSERT INTO users VALUES (?, ?)", [(1, "Alice"), (2, "Bob"), (3, "Charlie")])

cur.execute("SELECT * FROM users")

print(cur.fetchone())   # (1, 'Alice')
print(cur.fetchmany(2)) # [(2, 'Bob'), (3, 'Charlie')]
print(cur.fetchone())   # None(もうデータがない)

まとめ

  • fetch は SQL 実行結果を Python で扱える形に取り出す仕組み
  • fetchone は1件、fetchmany は指定件数、fetchall は全件取得
  • 内部的にはカーソルが結果セットの位置を保持し、呼ぶたびに順にデータを返す

fetch 系以外の結果取得方法

Python の sqlite3 では fetchone / fetchmany / fetchall 以外にも、カーソルオブジェクト自体を イテレータとして扱うことができます。これにより、ループで自然に行を取り出すことが可能です。

for ループでの反復処理

import sqlite3

conn = sqlite3.connect(":memory:")
cur = conn.cursor()
cur.execute("CREATE TABLE users (id INTEGER, name TEXT)")
cur.executemany("INSERT INTO users VALUES (?, ?)", [(1, "Alice"), (2, "Bob"), (3, "Charlie")])

cur.execute("SELECT * FROM users")

# カーソルを直接イテレート
for row in cur:
    print(row)

実行結果例:

(1, 'Alice')
(2, 'Bob')
(3, 'Charlie')

仕組み

  • cursor.execute() を実行した時点で結果セットがカーソルに保持される
  • for row in cur: と書くと、カーソルが イテレータプロトコルを実装しているため、順に1行ずつ取り出せる
  • 内部的には fetchone() を繰り返しているイメージに近い

他のテクニック

  1. リスト内包表記で一気にリスト化
cur.execute("SELECT * FROM users")
rows = [row for row in cur]
print(rows)  # [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie')]
  1. 辞書形式で取り出す (row_factory の活用)
    通常はタプル (id, name) で返ってきますが、row_factory を設定すると便利に扱えます。
conn.row_factory = sqlite3.Row
cur = conn.cursor()
cur.execute("SELECT * FROM users")
for row in cur:
    print(row["id"], row["name"])
  1. 次の1行をループで手動取得
cur.execute("SELECT * FROM users")
while True:
    row = cur.fetchone()
    if row is None:
        break
    print(row)

まとめ

  • for row in cur: はカーソルがイテレータとして動作する仕組みを利用している
  • 結果を一度に取り出す fetchall よりも、行ごとに処理する場合に効率的
  • row_factory を使えば辞書形式などで可読性を上げられる

row_factoryの仕組み

row_factory は、SQLite のクエリ結果をどんな形で返すかをカスタマイズできる仕組みです。

通常の動作

sqlite3 のデフォルトでは、SELECT の結果は タプル で返ってきます。

import sqlite3

conn = sqlite3.connect(":memory:")
cur = conn.cursor()
cur.execute("CREATE TABLE users (id INTEGER, name TEXT)")
cur.execute("INSERT INTO users VALUES (1, 'Alice')")

cur.execute("SELECT * FROM users")
print(cur.fetchone())  # (1, 'Alice')

この場合、列名を直接使えないため、インデックス番号でアクセスする必要があります。

row_factory の仕組み

Connection オブジェクトの row_factory 属性に「行をどう返すか」を決める関数やクラスを指定できます。

  • デフォルト: None(結果はタプル)
  • 例: sqlite3.Row を指定すると、辞書のように列名でアクセスできるオブジェクトを返すようになります。

辞書形式で使う例

conn = sqlite3.connect(":memory:")
conn.row_factory = sqlite3.Row  # 行の返し方を変更
cur = conn.cursor()

cur.execute("CREATE TABLE users (id INTEGER, name TEXT)")
cur.execute("INSERT INTO users VALUES (1, 'Alice')")

cur.execute("SELECT * FROM users")
row = cur.fetchone()

print(row["id"])    # 1
print(row["name"])  # Alice

sqlite3.Row を使うと、行をタプルのようにも辞書のようにも扱えます。

自作の row_factory

実は row_factory は任意の関数を渡すこともできます。
その関数は (cursor, row) を引数に受け取り、返したものが1行分の結果として使われます。

def dict_factory(cursor, row):
    return {col[0]: row[idx] for idx, col in enumerate(cursor.description)}

conn = sqlite3.connect(":memory:")
conn.row_factory = dict_factory
cur = conn.cursor()

cur.execute("CREATE TABLE users (id INTEGER, name TEXT)")
cur.execute("INSERT INTO users VALUES (1, 'Alice')")

cur.execute("SELECT * FROM users")
print(cur.fetchone())  # {'id': 1, 'name': 'Alice'}

まとめ

  • row_factory は「結果1行をどう返すか」を指定できる仕組み
  • デフォルトはタプル、sqlite3.Row を使うと辞書風アクセスが可能
  • 独自の関数を渡すことで、返り値のフォーマットを自由に設計できる

これを知っておくと、アプリ開発やデータ処理のときにコードがぐっと読みやすくなります。

execute系のテクニック

execute 系メソッドの基本

Python の sqlite3 で SQL を実行するときには cursor.execute() を中心に使います。これに加えて、複数行挿入やスクリプト実行に便利なバリエーションが用意されています。

execute

  • 単一の SQL 文を実行する基本メソッド
  • プレースホルダ(?)を使って値を安全にバインドできる
cur.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Alice", 25))

ここで ? がプレースホルダ。SQL インジェクション対策として文字列連結ではなく、必ずこれを使うのが推奨です。

executemany

  • 同じ SQL 文を繰り返し実行するときに効率的
  • 値のリストをまとめて渡せる
data = [("Bob", 30), ("Charlie", 22), ("Diana", 28)]
cur.executemany("INSERT INTO users (name, age) VALUES (?, ?)", data)

ループで execute を呼ぶよりも高速かつ簡潔に書けます。

executescript

  • 複数の SQL 文を「一つの文字列」としてまとめて実行できる
  • セミコロン区切りのスクリプトをそのまま渡せる
cur.executescript("""
DROP TABLE IF EXISTS users;
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);
INSERT INTO users (name, age) VALUES ('Eve', 20);
""")

DDL(テーブル作成や削除)と DML(データ操作)を一気に実行したいときに便利。
ただしプレースホルダは使えないため、ユーザー入力を直接埋め込むのは危険。

テクニックと注意点

  1. プレースホルダを必ず使う
    例:cur.execute("SELECT * FROM users WHERE age > ?", (20,))
    文字列結合で SQL を組み立てると SQL インジェクションのリスクがある。
  2. 複数値のプレースホルダ
    cur.execute("INSERT INTO users (id, name, age) VALUES (?, ?, ?)", (1, "Frank", 33))
  3. executemany でバルクインサート
    データ量が多いときに高速。数千件〜数万件でも効率的に処理できる。
  4. executescript で初期化処理
    テーブルを作成し直すときなど、スキーマとデータをまとめてセットアップできる。

まとめ

  • execute: 単一の SQL 実行。プレースホルダで安全に値を渡す
  • executemany: 同じ SQL を複数回効率よく実行
  • executescript: 複数の SQL 文をまとめて実行(ただしバインド不可)
  • SQL を安全に扱うには、常にプレースホルダを活用すること

プレースホルダに辞書型を使う方法

SQLite(Python の sqlite3 モジュール)では、プレースホルダに 名前付きパラメータを使う方法があり、辞書を渡して値をバインドできます。

名前付きプレースホルダの書き方

プレースホルダの指定方法には3種類ありますが、その中で辞書に対応しているのは :name 形式%(name)s 形式 です。

例1: :name 形式

import sqlite3

conn = sqlite3.connect(":memory:")
cur = conn.cursor()
cur.execute("CREATE TABLE users (id INTEGER, name TEXT, age INTEGER)")

# 名前付きプレースホルダ
cur.execute(
    "INSERT INTO users (id, name, age) VALUES (:id, :name, :age)",
    {"id": 1, "name": "Alice", "age": 25}
)

cur.execute("SELECT * FROM users")
print(cur.fetchone())  # (1, 'Alice', 25)

例2: %(name)s 形式

cur.execute(
    "INSERT INTO users (id, name, age) VALUES (%(id)s, %(name)s, %(age)s)",
    {"id": 2, "name": "Bob", "age": 30}
)

仕組み

  • SQL 文の中で :name%(name)s をプレースホルダとして書く
  • Python 側で辞書を渡すと、キー名に対応する値が自動的にバインドされる
  • 値は型安全に渡されるので SQL インジェクションのリスクを避けられる

注意点

  • ? プレースホルダの場合 → タプルやリストを渡す
  • :name%(name)s の場合 → 辞書を渡す
  • 両者を混在させることはできない

まとめ

  • 名前付きプレースホルダを使うと、コードの可読性が上がり引数の対応関係がわかりやすくなる
  • :name または %(name)s の形式を使い、対応する辞書を渡す
  • 大規模なクエリや列数が多いときに特に便利

conn.iterdump() とは

conn.iterdump() は、SQLite の データベース全体を SQL 文の形でダンプ(書き出し)する機能です。
現在のデータベースの状態(スキーマ定義やデータの挿入文)を、CREATE TABLEINSERT INTO の SQL 文に変換して順に返してくれます。

仕組み

  • iterdump() はイテレータを返す
  • ループで回すと、データベースを再構築できる SQL 文が1行ずつ取り出せる
  • 実態としては、sqlite3 が内部で sqlite_master などのメタ情報を読み出し、SQL を組み立てている

基本的な使い方

import sqlite3

# インメモリDBを作成
conn = sqlite3.connect(":memory:")
cur = conn.cursor()

# テーブル作成とデータ挿入
cur.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
cur.executemany("INSERT INTO users (name) VALUES (?)", [("Alice",), ("Bob",)])

# iterdumpでダンプ
for line in conn.iterdump():
    print(line)

出力例:

BEGIN TRANSACTION;
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO "users" VALUES(1,'Alice');
INSERT INTO "users" VALUES(2,'Bob');
COMMIT;

主な用途

  1. バックアップ
    • 生成された SQL 文をファイルに保存しておけば、別の環境で executescript() すれば再現可能。
  2. デバッグ
    • 今のデータベースの中身を SQL 文として確認できるので、テストやデバッグ時に役立つ。
  3. 移行・共有
    • SQLite のバイナリファイルを直接コピーできない場面で、SQL 文としてエクスポート・インポートする用途に使える。

まとめ

  • conn.iterdump() はデータベースの内容を 再現可能な SQL 文のストリームとして返す
  • for ループで1行ずつ取り出したり、ファイルに書き出すことでバックアップや移行に利用できる
  • executescript() と組み合わせればダンプした内容を別のDBに復元可能

wtih conn: という書き方で使う方法

with conn: の書き方は、Connection オブジェクトをコンテキストマネージャとして扱う機能を活用する方法です。Python の sqlite3 モジュールは Connection__enter__ / __exit__ が実装されているため、with 構文で安全に使えます。

挙動

import sqlite3

conn = sqlite3.connect("example.db")

with conn:  # コンテキストマネージャとして使用
    cur = conn.cursor()
    cur.execute("INSERT INTO users (name) VALUES (?)", ("Alice",))
  • with ブロックに入った時点では特に変化なし
  • with ブロックを抜けるときに
    • 例外が発生しなければ commit() が自動的に呼ばれる
    • 例外が発生すれば rollback() が自動的に呼ばれる

つまり、トランザクション処理を安全に包んでくれる仕組みです。

メリット

  1. commit/rollback の書き忘れ防止
    明示的に conn.commit() を書かなくても、成功時は自動的に確定。
  2. 例外処理が簡潔になる
    try-except-finally で rollback() を書く必要がない。
  3. コードの可読性が向上
    「このブロック内の処理がひとまとまりのトランザクション」という意図が明確になる。

デメリット・注意点

  • with conn: の外で同じ conn を使い続ける場合、commit のタイミングが自動で行われたことを意識しておく必要がある。
  • 自動コミットや複雑なトランザクション制御をしたい場合には向かない。

実践例

with sqlite3.connect("example.db") as conn:
    cur = conn.cursor()
    cur.execute("INSERT INTO users (name) VALUES (?)", ("Bob",))
    cur.execute("INSERT INTO users (name) VALUES (?)", ("Charlie",))
# 成功すれば自動commit、例外が出れば自動rollback

まとめ

  • with conn:トランザクション処理を安全に自動化できる便利な書き方
  • 成功時は commit、失敗時は rollback を自動で行う
  • 小規模〜中規模の処理やサンプルコードには特に向いている
  • 大規模で複雑なトランザクション管理をする場合は、明示的に commit/rollback を制御した方が安心

-Python
-