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 教程。