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