fetchone() 是 Python 数据库 API (DB-API 2.0) 中的一个标准方法,它用于从查询结果集中获取下一行数据,当没有更多数据时,它会返回 None,这个特性使得它非常适合用在 while 循环中。

核心概念
fetchone() 的工作方式就像一个迭代器:
- 执行
SELECT查询后,数据库会将结果集放在一个“游标”(cursor)的后面。 - 第一次调用
cursor.fetchone(),它会获取第一行数据,并将游标移动到第二行之前。 - 第二次调用,它会获取第二行数据,并将游标移动到第三行之前。
- 这个过程会一直持续,直到游标移动到所有数据的末尾。
- 当没有更多数据可获取时,
fetchone()会返回None。
我们可以利用 fetchone() 返回 None 这个特性来作为循环终止的条件。
基本语法和示例
下面是一个使用 fetchone() 循环的标准模板,我们将使用 sqlite3 作为示例,因为它内置在 Python 中,无需额外安装。
import sqlite3
# 1. 连接到数据库 (如果不存在,则会创建)
# 使用 'with' 语句可以确保连接在代码块执行完毕后自动关闭
try:
with sqlite3.connect('example.db') as conn:
# 2. 创建一个游标对象
cursor = conn.cursor()
# 3. 执行 SQL 查询
# 假设我们有一个名为 users 的表
cursor.execute("SELECT id, name, email FROM users")
# 4. 使用 fetchone() 循环获取结果
print("--- 开始获取用户数据 ---")
while True:
# 获取下一行数据
row = cursor.fetchone()
# fetchone() 返回 None,表示没有更多数据了,循环结束
if row is None:
break
# 5. 处理获取到的数据
# row 是一个元组,(1, 'Alice', 'alice@example.com')
user_id, name, email = row # 元组解包,使代码更清晰
print(f"ID: {user_id}, Name: {name}, Email: {email}")
print("--- 所有数据获取完毕 ---")
except sqlite3.Error as e:
print(f"数据库错误: {e}")
代码解析:

with sqlite3.connect(...) as conn:: 这是一个很好的实践,它会自动处理数据库连接的打开和关闭,即使在代码块中发生错误。cursor = conn.cursor(): 游标是执行 SQL 命令和获取结果的主要接口。cursor.execute(...): 执行你的 SQL 查询。while True:: 我们创建一个无限循环。row = cursor.fetchone(): 在循环的每一步,我们都尝试获取下一行。if row is None: break: 这是循环的“出口”,当fetchone()扫描完所有行后,它会返回None,if条件成立,break语句会跳出while循环。user_id, name, email = row: 将元组row中的元素解包到单独的变量中,这比使用索引(如row[0],row[1])更具可读性。
与其他 fetch* 方法的比较
了解 fetchone() 与其他方法的区别很重要,这有助于你根据场景选择最合适的方法。
| 方法 | 描述 | 适用场景 | 内存占用 |
|---|---|---|---|
fetchone() |
每次只获取一行数据。 | 逐行处理数据,尤其是在结果集非常大时,可以避免一次性加载所有数据到内存中。 | 低 |
fetchall() |
一次性获取所有剩余的行,返回一个列表。 | 当你知道结果集不大,并且需要一次性处理所有数据时,代码更简洁。 | 高 (取决于结果集大小) |
fetchmany(size) |
每次获取指定数量(size)的行,返回一个列表。 |
fetchone() 和 fetchall() 之间的折中方案,可以控制每次从数据库加载的数据量,平衡了内存使用和I/O效率。 |
中等 (由 size 参数控制) |
fetchall() 示例 (作为对比)
# ... (连接和执行查询的代码相同) ...
cursor.execute("SELECT id, name, email FROM users")
# 一次性获取所有行
all_rows = cursor.fetchall()
# 然后遍历这个列表
for row in all_rows:
user_id, name, email = row
print(f"ID: {user_id}, Name: {name}, Email: {email}")
完整的、可运行的示例
让我们创建一个完整的示例,包括创建表、插入数据和查询数据。
import sqlite3
def setup_database():
"""设置数据库并插入一些示例数据"""
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
# 创建表
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
)
''')
# 清空旧数据 (可选)
cursor.execute("DELETE FROM users")
# 插入一些数据
users_to_insert = [
(1, 'Alice', 'alice@example.com'),
(2, 'Bob', 'bob@example.com'),
(3, 'Charlie', 'charlie@example.com'),
(4, 'David', 'david@example.com')
]
cursor.executemany("INSERT INTO users (id, name, email) VALUES (?, ?, ?)", users_to_insert)
conn.commit()
conn.close()
def fetch_users_with_fetchone():
"""使用 fetchone() 循环获取并打印所有用户"""
print("\n--- 使用 fetchone() 获取用户 ---")
try:
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
cursor.execute("SELECT id, name, email FROM users ORDER BY id")
count = 0
while True:
row = cursor.fetchone()
if row is None:
break
count += 1
print(f"用户 {count}: ID={row[0]}, 姓名={row[1]}, 邮箱={row[2]}")
print(f"总共获取了 {count} 个用户。")
except sqlite3.Error as e:
print(f"数据库错误: {e}")
# --- 执行 ---
if __name__ == "__main__":
setup_database()
fetch_users_with_fetchone()
-
何时使用
fetchone()循环?- 当处理非常大的结果集时,这是首选,因为它能保持内存占用很低。
- 当你需要逐行处理数据,并且在处理完一行后立即进行其他操作(如写入文件、调用API等)时。
- 当你不确定结果集的大小,为了安全起见,避免内存溢出。
-
优点:
(图片来源网络,侵删)- 内存效率高: 任何时候内存中只有一行数据。
- 即时处理: 可以立即开始处理获取到的数据,无需等待整个查询完成。
-
缺点:
- 代码稍长: 相比
fetchall(),需要更多的样板代码(while循环和if判断)。 - 数据库交互次数多: 对每一行数据都进行一次数据库往返(虽然现代数据库和驱动程序对此有优化,但理论上比批量获取慢)。
- 代码稍长: 相比
