Python sqlite3.Connection.total_changes 属性
上次修改时间:2025 年 4 月 15 日
本综合指南探讨 Python 的 sqlite3.Connection.total_changes 属性,该属性跟踪自连接以来数据库行修改的总数。我们将介绍它的行为、使用模式和实际示例。
基本定义
SQLite 连接对象的 total_changes 属性返回自数据库连接打开以来已修改、插入或删除的数据库行的总数。
主要特点:它计算所有更改,包括来自触发器的更改;它是连接特定的;并且计数器在连接关闭时重置。 这与仅显示上次操作的 changes 不同。
total_changes 的基本用法
此示例演示了 total_changes 的基本用法,用于跟踪通过连接所做的修改。
import sqlite3
with sqlite3.connect(':memory:') as conn:
cursor = conn.cursor()
cursor.execute('CREATE TABLE test (id INTEGER, data TEXT)')
# Initial changes (table creation counts)
print(f"Initial changes: {conn.total_changes}")
cursor.execute("INSERT INTO test VALUES (1, 'First')")
cursor.execute("INSERT INTO test VALUES (2, 'Second')")
# Changes after inserts
print(f"After inserts: {conn.total_changes}")
cursor.execute("UPDATE test SET data = 'Updated' WHERE id = 1")
# Changes after update
print(f"After update: {conn.total_changes}")
此示例创建一个内存数据库,进行多次修改,并在每次操作后打印更改计数。 表创建在 SQLite 中也算作更改。
输出将显示递增的数字,因为每个操作都会影响数据库。 这是监控总体数据库活动的最简单方法。
跟踪事务内的更改
此示例显示了 total_changes 在事务中的行为,包括回滚。
import sqlite3
with sqlite3.connect(':memory:') as conn:
cursor = conn.cursor()
cursor.execute('CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)')
print(f"After create: {conn.total_changes}")
# First transaction
cursor.execute("INSERT INTO items (name) VALUES ('Apple')")
cursor.execute("INSERT INTO items (name) VALUES ('Banana')")
conn.commit()
print(f"After commit: {conn.total_changes}")
# Second transaction (rolled back)
cursor.execute("INSERT INTO items (name) VALUES ('Cherry')")
print(f"Before rollback: {conn.total_changes}")
conn.rollback()
print(f"After rollback: {conn.total_changes}")
这表明 total_changes 包括未提交的更改,直到发生回滚。 计数器随着每次操作而增加,但在回滚更改时减少。
回滚将计数器恢复到事务前的状态,表明 total_changes 反映了数据库的实际持久状态。
比较 total_changes 和 changes
此示例将 total_changes 与仅显示上次操作影响的 changes 属性进行对比。
import sqlite3
with sqlite3.connect(':memory:') as conn:
cursor = conn.cursor()
cursor.execute('CREATE TABLE logs (id INTEGER, message TEXT)')
print(f"Total changes: {conn.total_changes}")
print(f"Last changes: {conn.changes}")
cursor.executemany("INSERT INTO logs VALUES (?, ?)",
[(1, 'start'), (2, 'middle'), (3, 'end')])
print(f"After multi-insert - Total: {conn.total_changes}")
print(f"After multi-insert - Last: {conn.changes}")
cursor.execute("DELETE FROM logs WHERE id IN (1, 3)")
print(f"After delete - Total: {conn.total_changes}")
print(f"After delete - Last: {conn.changes}")
主要区别在于 changes 仅显示最近操作的行数,而 total_changes 累积所有更改。 这在多行操作中尤其明显。
changes 属性对于立即反馈很有用,而 total_changes 提供了一个连接范围的活动指标。
触发器对 total_changes 的影响
此示例演示了触发器如何影响 total_changes 计数,因为它们执行的额外操作会被计数。
import sqlite3
with sqlite3.connect(':memory:') as conn:
cursor = conn.cursor()
# Create tables and trigger
cursor.execute('CREATE TABLE orders (id INTEGER, amount REAL)')
cursor.execute('CREATE TABLE audit (order_id INTEGER, change TEXT)')
cursor.execute('''CREATE TRIGGER log_order AFTER INSERT ON orders
BEGIN
INSERT INTO audit VALUES (NEW.id, 'Order created');
END''')
print(f"After setup: {conn.total_changes}")
# Insert that fires the trigger
cursor.execute("INSERT INTO orders VALUES (1, 99.99)")
print(f"After insert: {conn.total_changes}")
# Verify both tables were modified
print("Orders:", cursor.execute("SELECT * FROM orders").fetchall())
print("Audit:", cursor.execute("SELECT * FROM audit").fetchall())
触发器导致将额外插入到审计表中,这意味着单个 INSERT 操作会导致两个计数更改。 这表明 total_changes 如何捕获所有数据库修改。
当存在触发器时,了解此行为非常重要,因为更改计数可能高于仅来自直接操作的预期。
监控批量操作
此示例显示了如何使用 total_changes 来监控大型批量操作并提供进度反馈。
import sqlite3
import random
with sqlite3.connect(':memory:') as conn:
cursor = conn.cursor()
cursor.execute('CREATE TABLE readings (id INTEGER, value REAL)')
# Insert 1000 random readings
for i in range(1, 1001):
cursor.execute("INSERT INTO readings VALUES (?, ?)",
(i, random.uniform(0, 100)))
if i % 100 == 0:
print(f"Inserted {i} rows, total changes: {conn.total_changes}")
# Delete half the records
cursor.execute("DELETE FROM readings WHERE id % 2 = 0")
print(f"After delete: {conn.total_changes}")
这种模式对于需要提供进度更新的长时间运行的操作非常有用。 total_changes 提供了迄今为止所有修改的准确计数。
该示例还表明,删除的计数方式与插入相同,删除的每一行都会递增计数器。
重置 total_changes
虽然 total_changes 无法直接重置,但此示例显示了如何通过使用单独的连接来有效地创建新基线。
import sqlite3
# First connection with initial changes
with sqlite3.connect('test.db') as conn1:
cursor = conn1.cursor()
cursor.execute('CREATE TABLE IF NOT EXISTS temp (id INTEGER)')
cursor.execute('INSERT INTO temp VALUES (1)')
print(f"Connection 1 changes: {conn1.total_changes}")
# Second connection starts fresh
with sqlite3.connect('test.db') as conn2:
cursor = conn2.cursor()
print(f"Connection 2 initial: {conn2.total_changes}")
cursor.execute('INSERT INTO temp VALUES (2)')
print(f"Connection 2 after insert: {conn2.total_changes}")
每个新连接都以 total_changes 计数为零开始。 此示例显示了如何在需要时通过创建新连接来有效地“重置”跟踪。
当您希望分别监控应用程序执行的特定阶段中的更改时,此方法很有用。
性能注意事项
此示例表明,访问 total_changes 对性能的影响最小,可以安全地频繁使用。
import sqlite3
import time
with sqlite3.connect(':memory:') as conn:
cursor = conn.cursor()
cursor.execute('CREATE TABLE test (id INTEGER)')
start_time = time.time()
for i in range(10000):
cursor.execute("INSERT INTO test VALUES (?)", (i,))
changes = conn.total_changes # Access in each iteration
duration = time.time() - start_time
print(f"Inserted {conn.total_changes} rows in {duration:.3f} seconds")
print(f"Average time per insert: {(duration*1000)/10000:.3f} ms")
total_changes 计数器由 SQLite 在内部维护,因此访问它非常高效。 此示例表明,频繁访问只会增加可忽略不计的开销。
这使得它可以在对性能敏感的代码中使用,在这些代码中您需要监控数据库活动而不会产生重大影响。
最佳实践
- 用于监控: 非常适合跟踪总体数据库活动
- 了解范围: 计算所有更改,包括触发器
- 连接特定: 每个连接维护自己的计数
- 与 changes 结合使用: 使用两个属性来获得完整的图像
- 无法重置: 如果需要新的计数,请创建新连接
资料来源
作者
列出所有 Python 教程。