Python sqlite3.Cursor.lastrowid
上次修改时间:2025 年 4 月 15 日
本综合指南探讨了 Python 的 sqlite3.Cursor.lastrowid
属性,该属性提供最后插入行的行 ID。我们将介绍基本用法、常用模式和实际示例。
基本定义
SQLite 游标的 lastrowid
属性返回由 INSERT 操作修改的最后一行的行 ID。 当使用自动递增主键时,这特别有用。
主要特点:它是只读的,如果没有插入行则返回 None,并且仅适用于具有 INTEGER PRIMARY KEY 列的表。 该值是数据库特定的和连接特定的。
lastrowid 的基本用法
这是 lastrowid
的最简单用法,用于获取插入行的 ID。 我们将使用上下文管理器进行自动资源清理。
import sqlite3 with sqlite3.connect('example.db') as conn: cursor = conn.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)''') cursor.execute("INSERT INTO users (name, email) VALUES (?, ?)", ('Alice', 'alice@example.com')) print("Last inserted row ID:", cursor.lastrowid)
此示例创建一个具有自动递增 ID 列的表,插入一行,并打印新插入行的 ID。 with
语句确保正确清理。
lastrowid
在 INSERT 语句之后立即可用,并且在同一游标上执行另一个 INSERT 之前仍然可以访问。
多次插入操作
执行多次插入时,lastrowid
始终反映最近的插入操作。 此示例演示了此行为。
import sqlite3 with sqlite3.connect('example.db') as conn: cursor = conn.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, name TEXT, price REAL)''') # First insert cursor.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Laptop', 999.99)) print("First insert ID:", cursor.lastrowid) # Second insert cursor.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Phone', 699.99)) print("Second insert ID:", cursor.lastrowid)
每个 INSERT 操作都会更新 lastrowid
值。 输出显示按顺序分配给每个新行的 ID。
即使插入在不同的事务中,或者插入之间发生其他操作,此行为也是一致的。
批量插入操作
当使用 executemany
进行批量插入时,lastrowid
仅返回批处理中最后插入行的 ID。
import sqlite3 employees = [ ('John Doe', 'Engineering'), ('Jane Smith', 'Marketing'), ('Bob Johnson', 'Sales') ] with sqlite3.connect('company.db') as conn: cursor = conn.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS employees (id INTEGER PRIMARY KEY, name TEXT, department TEXT)''') cursor.executemany("INSERT INTO employees (name, department) VALUES (?, ?)", employees) print("Last inserted row ID from bulk operation:", cursor.lastrowid)
executemany
方法插入多行,但只有最后插入行的 ID 在 lastrowid
中可用。
要获取所有插入的 ID,您需要执行单独的 INSERT 语句,或者在每次插入后使用不同的方法,例如 SELECT last_insert_rowid
。
没有自动递增的表
当使用没有 INTEGER PRIMARY KEY 列的表时,lastrowid
的行为会有所不同。 此示例显示了差异。
import sqlite3 with sqlite3.connect('test.db') as conn: cursor = conn.cursor() # Table with explicit primary key cursor.execute('''CREATE TABLE IF NOT EXISTS table1 (pk TEXT PRIMARY KEY, data TEXT)''') cursor.execute("INSERT INTO table1 VALUES ('key1', 'value1')") print("Table with TEXT PK:", cursor.lastrowid) # None # Table with composite primary key cursor.execute('''CREATE TABLE IF NOT EXISTS table2 (id INT, name TEXT, PRIMARY KEY (id, name))''') cursor.execute("INSERT INTO table2 VALUES (1, 'test')") print("Table with composite PK:", cursor.lastrowid) # None # Table with INTEGER PRIMARY KEY cursor.execute('''CREATE TABLE IF NOT EXISTS table3 (id INTEGER PRIMARY KEY, data TEXT)''') cursor.execute("INSERT INTO table3 (data) VALUES ('works')") print("Table with INTEGER PK:", cursor.lastrowid) # Actual ID
只有具有 INTEGER PRIMARY KEY 列(默认情况下自动递增)的表才会填充 lastrowid
。 其他主键类型导致 None。
在设计需要跟踪插入行 ID 的数据库模式时,这种区别非常重要。
将 lastrowid 与事务一起使用
即使事务回滚,lastrowid
仍然有效,但 ID 可能会被重用。 此示例演示了该行为。
import sqlite3 with sqlite3.connect('transactions.db') as conn: cursor = conn.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS logs (id INTEGER PRIMARY KEY, message TEXT)''') # Successful transaction cursor.execute("INSERT INTO logs (message) VALUES ('First message')") first_id = cursor.lastrowid conn.commit() print("First ID (committed):", first_id) # Rolled back transaction cursor.execute("INSERT INTO logs (message) VALUES ('Second message')") second_id = cursor.lastrowid conn.rollback() print("Second ID (rolled back):", second_id) # New insert after rollback cursor.execute("INSERT INTO logs (message) VALUES ('Third message')") third_id = cursor.lastrowid conn.commit() print("Third ID (committed):", third_id)
回滚后,下一个插入可能会重用回滚的 ID。 确切的行为取决于 SQLite 的内部序列计数器。
当事务可能回滚时,应用程序不应依赖于特定的 ID 序列。
将 lastrowid 与 SELECT 结合使用
您可以将 lastrowid
与 SELECT 语句结合使用,以检索完整的插入行。 当您需要的不仅仅是 ID 时,这很有用。
import sqlite3 with sqlite3.connect('inventory.db') as conn: cursor = conn.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT, quantity INTEGER)''') # Insert new item cursor.execute("INSERT INTO items (name, quantity) VALUES (?, ?)", ('Widget', 100)) new_id = cursor.lastrowid # Retrieve the complete inserted row cursor.execute("SELECT * FROM items WHERE id = ?", (new_id,)) inserted_row = cursor.fetchone() print("Inserted row:", inserted_row)
这种模式是有效的,因为它使用主键索引来检索刚刚插入的确切行。
当默认值或触发器在插入期间修改数据时,它特别有用。
lastrowid 的替代方案
SQLite 提供了 last_insert_rowid
函数作为 cursor.lastrowid
的替代方案。 此示例显示了两种方法。
import sqlite3 with sqlite3.connect('alternate.db') as conn: cursor = conn.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, content TEXT)''') # Using cursor.lastrowid cursor.execute("INSERT INTO notes (content) VALUES ('First note')") print("Using cursor.lastrowid:", cursor.lastrowid) # Using last_insert_rowid() function cursor.execute("INSERT INTO notes (content) VALUES ('Second note')") cursor.execute("SELECT last_insert_rowid()") print("Using last_insert_rowid():", cursor.fetchone()[0])
两种方法都返回相同的值,但 last_insert_rowid
是一个 SQL 函数,可以在更复杂的查询中使用。
对于简单的情况,lastrowid
属性通常更方便,因为它不需要额外的查询。
最佳实践
- 与 INTEGER PRIMARY KEY 一起使用: lastrowid 仅适用于这些列
- 检查 None: 在使用之前,始终验证 lastrowid 是否不是 None
- 立即访问: 插入后立即检索 lastrowid
- 事务感知: 了解回滚时的行为
- 替代方法: 在需要时考虑 last_insert_rowid()
资料来源
作者
列出所有 Python 教程。