データベースの作成
データベースを新規作成
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
)は、共通のインターフェースを持っています。
- 接続 (Connection)
conn = module.connect(...)
データベースに接続するためのオブジェクトを返す。 - カーソル (Cursor)
cur = conn.cursor()
SQL を実行するためのオブジェクト。 - SQL 実行
cur.execute("SELECT * FROM users WHERE id = %s", (1,))
プレースホルダ付きで安全に SQL を実行できる。 - 結果取得
rows = cur.fetchall()
複数行・単一行・一部だけの結果取得方法が標準化。 - トランザクション管理
conn.commit()
conn.rollback()
これも共通のインターフェースで扱える。
- 例外クラス
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 テーブル名 (
列名 データ型 制約,
列名 データ型 制約,
...
);
主な要素
- テーブル名
- データを格納する表の名前。
- 例:
users
,products
など。
- 列名
- 各列の識別子。テーブルのカラムに相当。
- データ型
SQLite の場合は柔軟で、代表的な型は以下の通り。INTEGER
(整数)REAL
(浮動小数点)TEXT
(文字列)BLOB
(バイナリデータ)
- 制約
列やテーブルにかけるルール。代表的なもの: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, ...);
主な書き方
- 列を指定して挿入
INSERT INTO users (id, name, email, age) VALUES (1, 'Alice', 'alice@example.com', 25);
- すべての列に挿入
列リストを省略すると、テーブル定義にある全列に対応する値を順番通りに指定する必要があります。
INSERT INTO users VALUES (2, 'Bob', 'bob@example.com', 30);
- 複数行を一度に挿入
SQLite では複数行をまとめて挿入できます。
INSERT INTO users (name, email, age) VALUES
('Charlie', 'charlie@example.com', 22),
('Diana', 'diana@example.com', 28);
- 一部の列だけに挿入
指定しなかった列は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()
注意点
- 必要に応じて明示的に
conn.commit()
を呼ぶ- Python の
sqlite3
モジュールを使うときは 明示的に commit するまで変更が確定されません。データを永続化したいときは必ず呼ぶ
- Python の
- 一時的なデータ(テストなど)なら commit せず終了しても良い
- 自動コミットモードを有効化する
接続時に以下のように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
- 目的: データの追加・更新・削除などを行う
- 注意点: トランザクション管理が必要。
commit
やrollback
で確定や取り消しを行う
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
です。
代表的なメソッド
fetchone()
- 結果セットから次の1行を返す
- 返す行がなければ
None
を返す
cur.execute("SELECT * FROM users")
row = cur.fetchone()
print(row) # (1, 'Alice', 'alice@example.com', 25)
fetchmany(size)
- 指定した件数の行をまとめて取得
- 結果が残り少ない場合はそれ以下の行数になる
cur.execute("SELECT * FROM users")
rows = cur.fetchmany(2)
print(rows) # [(1, 'Alice', ...), (2, 'Bob', ...)]
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()
を繰り返しているイメージに近い
他のテクニック
- リスト内包表記で一気にリスト化
cur.execute("SELECT * FROM users")
rows = [row for row in cur]
print(rows) # [(1, 'Alice'), (2, 'Bob'), (3, 'Charlie')]
- 辞書形式で取り出す (
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行をループで手動取得
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(データ操作)を一気に実行したいときに便利。
ただしプレースホルダは使えないため、ユーザー入力を直接埋め込むのは危険。
テクニックと注意点
- プレースホルダを必ず使う
例:cur.execute("SELECT * FROM users WHERE age > ?", (20,))
文字列結合で SQL を組み立てると SQL インジェクションのリスクがある。 - 複数値のプレースホルダ
cur.execute("INSERT INTO users (id, name, age) VALUES (?, ?, ?)", (1, "Frank", 33))
- executemany でバルクインサート
データ量が多いときに高速。数千件〜数万件でも効率的に処理できる。 - 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 TABLE
や INSERT 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;
主な用途
- バックアップ
- 生成された SQL 文をファイルに保存しておけば、別の環境で
executescript()
すれば再現可能。
- 生成された SQL 文をファイルに保存しておけば、別の環境で
- デバッグ
- 今のデータベースの中身を SQL 文として確認できるので、テストやデバッグ時に役立つ。
- 移行・共有
- 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()
が自動的に呼ばれる
- 例外が発生しなければ
つまり、トランザクション処理を安全に包んでくれる仕組みです。
メリット
- commit/rollback の書き忘れ防止
明示的にconn.commit()
を書かなくても、成功時は自動的に確定。 - 例外処理が簡潔になる
try-except-finally でrollback()
を書く必要がない。 - コードの可読性が向上
「このブロック内の処理がひとまとまりのトランザクション」という意図が明確になる。
デメリット・注意点
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
を制御した方が安心