批量导入通常希望保留已经成功的批次,同时让包含坏数据的一批整体回滚。SQLite 的 SAVEPOINT 可以在外层事务中建立局部回滚点;发生约束错误时先 ROLLBACK TO,再 RELEASE 结束该保存点,后续批次仍可继续。
环境:Python 3.11+;Linux、macOS 或 Windows;仅使用标准库 sqlite3。
创建 savepoint_demo.py:
import sqlite3
connection = sqlite3.connect(":memory:")
connection.execute(
"CREATE TABLE jobs (name TEXT PRIMARY KEY, state TEXT NOT NULL)"
)
batches = [
[("alpha", "ready"), ("beta", "ready")],
[("gamma", "ready"), ("alpha", "duplicate")],
[("delta", "ready")],
]
with connection:
for number, rows in enumerate(batches, start=1):
connection.execute("SAVEPOINT current_batch")
try:
connection.executemany(
"INSERT INTO jobs(name, state) VALUES (?, ?)",
rows,
)
except sqlite3.IntegrityError as error:
connection.execute("ROLLBACK TO current_batch")
connection.execute("RELEASE current_batch")
print(f"batch {number}: skipped ({error})")
else:
connection.execute("RELEASE current_batch")
print(f"batch {number}: imported")
for row in connection.execute("SELECT name, state FROM jobs ORDER BY name"):
print(row)
connection.close()
运行:
python savepoint_demo.py
第二批中的 alpha 违反主键约束,因此同批先插入的 gamma 也会被撤销;第一批和第三批不受影响,最终只输出 alpha、beta、delta 三行记录。
这里 ROLLBACK TO 只回到保存点,并不会关闭保存点,所以异常分支仍要执行 RELEASE。外层 with connection: 负责最终提交;若循环之外再抛出未处理异常,外层事务仍会整体回滚。若业务要求任何一批失败都取消全部导入,则不应捕获后继续,而应让异常离开 with 块。