Python conn.commit()とは?sqlite3で変更を確定する方法と忘れたときに起きること

Pythonのconn.commit()は、データベース接続(conn)で実行したINSERT・UPDATE・DELETEなどの変更を確定して保存するメソッドです。sqlite3の標準設定では、commit()を呼ばずに接続を閉じると、その変更はすべて取り消されます。

connは「connection(接続)」を略した変数名で、conn = sqlite3.connect("app.db")のように作った接続オブジェクトを指すのが一般的です。この記事では、sqlite3を例にcommit()が必要な理由、忘れたときに起きること、rollback()・with conn:・Python 3.12で加わったautocommitとの関係を、Python 3.12で実際に動かした結果つきで説明します。sqlite3の基本操作全体はPython sqlite3の使い方|execute・fetchall・commitにまとめています。


conn.commit()とは?

sqlite3の公式ドキュメントでは、commit()は「保留中のトランザクションをデータベースにコミットする」メソッドで、autocommitがTrueのときや、開いているトランザクションがないときは何もしない、と説明されています(2026年10月18日確認、Python 3.15.0のドキュメント)。

トランザクションとは「まとめて成功するか、まとめて取り消すか」の単位です。sqlite3の標準設定では、INSERT・UPDATE・DELETE・REPLACEを実行した時点で自動的にトランザクションが始まり、commit()を呼ぶまで確定しません。考え方はトランザクションとは?で解説しています。

commit()を忘れるとどうなるか

実際に試すと、データが消えます。

import sqlite3

conn = sqlite3.connect("app.db")
conn.execute("CREATE TABLE users (name TEXT)")
conn.execute("INSERT INTO users VALUES ('sato')")
print("同じ接続では見える:", conn.execute("SELECT * FROM users").fetchall())
print("in_transaction:", conn.in_transaction)
conn.close()

conn2 = sqlite3.connect("app.db")
print("開き直すと:", conn2.execute("SELECT * FROM users").fetchall())
conn2.close()

実行結果:

同じ接続では見える: [('sato',)]
in_transaction: True
開き直すと: []

やっかいなのは、同じ接続の中では追加した行が見えることです。実行中は正常に動いているように見えるので、気づくのが遅れます。in_transactionがTrueなら「まだ確定していない変更がある」という意味です。DB-APIの仕様(PEP 249)でも、コミットせずに接続を閉じると暗黙のロールバックが行われると定められています。

commit()すれば保存される

import sqlite3

conn = sqlite3.connect("app.db")
conn.execute("CREATE TABLE users (name TEXT)")
conn.execute("INSERT INTO users VALUES ('sato')")
conn.commit()
print("in_transaction:", conn.in_transaction)
conn.close()

conn2 = sqlite3.connect("app.db")
print(conn2.execute("SELECT * FROM users").fetchall())
conn2.close()

実行結果(app.dbを削除してから実行):

in_transaction: False
[('sato',)]

データを変える処理のあとにconn.commit()を1行足すだけです。cursor.execute()で実行した場合も、コミットするのはカーソルではなく接続(conn)です。

操作 commit()が必要か(標準設定)
INSERT/UPDATE/DELETE/REPLACE 必要(ここでトランザクションが始まる)
SELECT 不要(データを変えない)
CREATE TABLEなど(トランザクションの外で実行) 不要(その場で確定)
CREATE TABLEなど(INSERTのあと、未コミットの状態で実行) 必要(同じトランザクションに含まれる)
executescript() 実行前に保留中のトランザクションを自動でコミットする

「テーブルは残るのにデータが消える」理由

最初の例で、INSERTした行は消えたのにテーブルは残っていました。これは、CREATE TABLEを実行した時点ではトランザクションが始まっておらず、SQLiteがその場で確定したためです。「CREATE TABLEは必ず自動でコミットされる」わけではありません。公式ドキュメントには「3.6より前のsqlite3は、DDL文の前で開いているトランザクションを暗黙にコミットしていたが、現在はそうしない」と書かれています。

import sqlite3

conn = sqlite3.connect("app.db")
conn.execute("CREATE TABLE users (name TEXT)")      # トランザクション外 → すぐ確定
conn.execute("INSERT INTO users VALUES ('sato')")   # ここでトランザクション開始
conn.execute("CREATE TABLE logs (msg TEXT)")        # 同じトランザクションの中
conn.close()                                         # commitせずに閉じる

conn2 = sqlite3.connect("app.db")
print(conn2.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall())
conn2.close()

実行結果:

[('users',)]

INSERTのあとに作ったlogsテーブルは、行と一緒に消えました。順番によって結果が変わるので、「変更したら最後にcommit()」と覚えておくのが安全です。

取り消すときはrollback()

import sqlite3

conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE users (name TEXT)")
conn.execute("INSERT INTO users VALUES ('sato')")
conn.commit()
conn.execute("INSERT INTO users VALUES ('suzuki')")
print("rollback前:", conn.execute("SELECT * FROM users").fetchall())
conn.rollback()
print("rollback後:", conn.execute("SELECT * FROM users").fetchall())
conn.close()

実行結果:

rollback前: [('sato',), ('suzuki',)]
rollback後: [('sato',)]

直前のcommit()の時点まで戻ります。確定済みのsatoは残り、未確定のsuzukiだけが消えました。

with conn:で自動commit・自動rollback

接続をwith文で使うと、ブロックを正常に抜けたときにコミット、例外で抜けたときにロールバックしてくれます。

import sqlite3

conn = sqlite3.connect("app.db")
conn.execute("CREATE TABLE users (name TEXT)")
with conn:
    conn.execute("INSERT INTO users VALUES ('sato')")
try:
    with conn:
        conn.execute("INSERT INTO users VALUES ('suzuki')")
        raise ValueError("途中で失敗")
except ValueError as e:
    print("例外:", e)
print("with後も使える:", conn.execute("SELECT * FROM users").fetchall())
conn.close()

実行結果:

例外: 途中で失敗
with後も使える: [('sato',)]

注意点は、sqlite3のwith conn:は接続を閉じないことです(公式ドキュメントにも明記)。ファイルのwith open()とは動きが違うので、最後にconn.close()を書くか、contextlib.closing()で包みます。閉じ方はConnection.close()とは?で説明しています。

import sqlite3
from contextlib import closing

with closing(sqlite3.connect("app.db")) as conn:   # 外側:最後にclose
    with conn:                                      # 内側:commit/rollback
        conn.execute("INSERT INTO users VALUES ('tanaka')")

Python 3.12以降のautocommit属性

Python 3.12で、sqlite3.connect()にautocommit引数(接続のautocommit属性)が加わりました。公式ドキュメントによると値は3種類で、標準は以前と同じ動きのLEGACY_TRANSACTION_CONTROL、推奨はFalseとされています(2026年10月18日確認)。

autocommitの値 動き commit()
sqlite3.LEGACY_TRANSACTION_CONTROL(標準) 3.11以前と同じ。INSERTなどの前に自動でトランザクションを開始 必要
False(推奨) 常にトランザクションが開いている(PEP 249準拠) 必要。DDLも含めて確定するまで保存されない
True SQLiteの自動コミットモード。1文ごとに確定 呼んでも何も起きない
import sqlite3

conn = sqlite3.connect("app.db", autocommit=True)
conn.execute("CREATE TABLE users (name TEXT)")
conn.execute("INSERT INTO users VALUES ('sato')")
conn.close()   # commit()を呼んでいない

conn2 = sqlite3.connect("app.db")
print(conn2.execute("SELECT * FROM users").fetchall())
print(conn2.autocommit == sqlite3.LEGACY_TRANSACTION_CONTROL)
conn2.close()

実行結果:

[('sato',)]
True

autocommit=Trueならcommit()なしでも保存されます。2行目のTrueは、引数を指定しない接続が標準のLEGACY_TRANSACTION_CONTROLになっていることを示しています。autocommit=Falseでは接続した直後からトランザクションが開いていて、commit()のあとも新しいトランザクションが自動で始まります(in_transactionが接続直後もcommit後もTrueになることを確認しました)。

commit()はループの外でまとめて

1件ごとにcommit()すると、そのたびにディスクへの書き込みが確定処理されるので遅くなります。2,000件のINSERTで比べました。

import sqlite3, time

def run(commit_each):
    conn = sqlite3.connect("bench.db")
    conn.execute("DROP TABLE IF EXISTS t")
    conn.execute("CREATE TABLE t (n INTEGER)")
    start = time.perf_counter()
    for i in range(2000):
        conn.execute("INSERT INTO t VALUES (?)", (i,))
        if commit_each:
            conn.commit()
    conn.commit()
    elapsed = time.perf_counter() - start
    conn.close()
    return elapsed

print(f"1件ごとにcommit: {run(True):.3f}秒")
print(f"最後に1回commit: {run(False):.3f}秒")

実行結果(筆者のMacでの一例。環境によって数値は変わります):

1件ごとにcommit: 0.476秒
最後に1回commit: 0.001秒

大量に入れるならexecutemany()でまとめて実行し、最後に1回commit()するのが基本です。件数が非常に多く途中で失敗したときのやり直しを小さくしたい場合は、1,000件ごとなど区切ってコミットします。

MySQL・PostgreSQLでもconn.commit()は必要?

PythonのDB接続ライブラリの多くはDB-API(PEP 249)に従っており、PEP 249は「データベースに自動コミット機能がある場合も、初期状態ではオフにしなければならない」と定めています。そのため、PostgreSQL用のpsycopgやMySQL用のドライバでも、基本はconn.commit()が必要です。psycopg 3の公式ドキュメントでも、commitせずに閉じた例で「INSERTは破棄された」と説明されています。

ただし、with文の動きはライブラリごとに違います。psycopg 3のwith psycopg.connect() as conn:はブロックの終わりにコミット(例外ならロールバック)したうえで接続も閉じますが、sqlite3のwith conn:は閉じません。使うライブラリのドキュメントで確認してください。

よくある質問

Q. conn.commit()を呼ばないとどうなりますか?

sqlite3の標準設定では、INSERT・UPDATE・DELETEの変更が確定しないまま残り、接続を閉じると取り消されます。同じ接続の中では変更が見えるため、実行中は気づきにくい点に注意してください。

Q. cursor.commit()ではなくconn.commit()なのはなぜですか?

トランザクションは接続単位で管理されるためです。カーソルでexecute()しても、確定や取り消しは接続オブジェクトのcommit()・rollback()で行います。

Q. with conn:を使えばcommit()は不要ですか?

ブロックを正常に抜けたときに自動でコミットされるので、ブロック内の変更についてはcommit()を書く必要はありません。ただしsqlite3のwith conn:は接続を閉じないので、close()は別に必要です。

Q. SELECTだけのときもcommit()が必要ですか?

標準設定ではSELECTだけならトランザクションは始まらないので不要です。autocommit=Falseで接続している場合は常にトランザクションが開いているため、長く接続を保つならcommit()かrollback()で区切ります。

まとめ

  • conn.commit()は接続で行った変更を確定する。呼ばずに閉じると取り消される
  • 同じ接続では未確定の変更も見えるので、in_transactionで確認できる
  • CREATE TABLEが自動で確定するのはトランザクションの外で実行したときだけ
  • with conn:は自動でcommit/rollbackするが、接続は閉じない
  • Python 3.12以降はautocommit引数で動きを選べる(推奨はFalse)
  • 大量の書き込みはループの外で1回commitすると速い

あわせて読みたい

対面・無料|東京・永田町で毎週土曜日13時

1回の参加で、プログラミングの構造がわかる

ゼロからはじめるPython入門講座

8年間・累計600回開催/これまで全国で2万人以上が参加

「やはり対面で聞けるのはとても気づきがあり、価値があると思います」

参加者の声

  • 本当に初心者からの講座です。プログラミングに触ったことがなくて大丈夫です
  • 対面だから、その場で聞けます。つまずいた所を持ち帰らずに解決できます
  • 講座は1時間。そのあとも残って学習を続けられ、講師にも質問できます
  • 独学で挫折した方も。糸口が見つかって、講座のあと会員になる方が多くいます

無料で参加する|開催日程を見る

参加費は無料です