登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  python教程

Python sqlite3.Row 怎样按列名读取查询结果

来源:17golang原创

时间:2026-11-02 11:55:37 197浏览 收藏

Python 的 sqlite3 查询默认返回元组,所以 row[0] 能取到第一列,但 row["name"] 会失败。要按列名读取,只需在创建游标之前把连接的 row_factory 设置为 sqlite3.Row;之后用 row["列名"]、row[索引] 或 row.keys() 访问即可。

官方文档:https://docs.python.org/3/library/sqlite3.html

核心写法是 con = sqlite3.connect(":memory:") 后执行 con.row_factory = sqlite3.Row。这个设置只会影响之后从该连接创建的游标,已有游标不会自动切换。

默认元组为什么不适合按列名读取

查询结果的形状由连接或游标上的 row_factory 决定。默认值是 None,因此每一行是元组,列顺序完全由 SELECT 子句决定。元组适合位置固定、字段很少的场景,但当 SQL 增加列或调整投影顺序时,连续的数字下标容易变得不直观。

sqlite3.Row 是内置的行工厂,支持迭代、按索引访问和按列名映射访问。它不是普通字典,不能把所有字典方法都当作可用接口;需要列名清单时,使用它提供的 keys()。

Python sqlite3 默认元组切换为 sqlite3.Row 的静态关系
图1:连接、row_factory、游标和查询行之间的结构说明图,不是运行截图。

先在连接上设置 sqlite3.Row

推荐把设置放在连接初始化阶段。这样从该连接后续创建的游标都会采用相同的行表示,业务函数不需要反复转换结果。

import sqlite3

# 连接级设置会传给之后创建的游标,适合统一约定查询结果形状。
con = sqlite3.connect(":memory:")
con.row_factory = sqlite3.Row

# 用别名让返回对象拥有稳定、可读的列名。
con.execute("CREATE TABLE user_profile (user_id INTEGER, display_name TEXT)")
con.execute(
    "INSERT INTO user_profile (user_id, display_name) VALUES (?, ?)",
    (7, "林默"),
)

# 查询返回 Row,而不是普通 tuple。
row = con.execute(
    "SELECT user_id, display_name FROM user_profile WHERE user_id = ?",
    (7,),
).fetchone()

if row is not None:
    # 列名访问表达业务字段;索引访问仍然保留,便于兼容旧代码。
    print(row["display_name"])
    print(row[0])

# 连接不再使用时显式关闭,避免把资源生命周期交给垃圾回收。
con.close()

这里使用参数占位符传入用户编号,避免把外部输入拼进 SQL。fetchone() 找不到记录时返回 None,所以按列名读取前仍要先判断结果是否存在。

按列名读取时,列名来自 SELECT 结果

Row 的访问键不是 Python 变量名,而是查询结果中的列名。推荐在投影中明确写出字段,并在表达式或联表场景中使用 AS 起别名:

# 别名决定 Row 中的键,避免把表达式原文当作业务字段名。
row = con.execute(
    "SELECT user_id AS id, display_name AS name "
    "FROM user_profile WHERE user_id = ?",
    (7,),
).fetchone()

if row is not None:
    # keys() 返回当前查询结果的列名列表,可用于展示或调试。
    print(row.keys())
    print(row["id"], row["name"])

官方文档说明,列名访问不区分大小写,因此 row["name"] 与 row["NAME"] 可以指向同一列。但工程上仍建议统一 SQL 别名大小写,避免不同数据库或人工维护时产生阅读差异。

老游标为什么仍然返回元组

一个容易被忽略的迁移边界是设置时机。连接的 row_factory 是新游标的初始配置;如果游标已经创建,再修改连接属性,已有游标不会自动更新。

import sqlite3

con = sqlite3.connect(":memory:")
old_cursor = con.cursor()

# 这个设置不会回溯修改已经创建的 old_cursor。
con.row_factory = sqlite3.Row
new_cursor = con.cursor()

# 旧游标与新游标的行类型可能不同;迁移时应统一创建顺序。
old_cursor.execute("SELECT 1 AS value")
new_cursor.execute("SELECT 1 AS value")

old_row = old_cursor.fetchone()
new_row = new_cursor.fetchone()
print(type(old_row).__name__, type(new_row).__name__)

old_cursor.close()
new_cursor.close()
con.close()

如果项目封装了连接池或游标工厂,最稳妥的做法是在连接交给业务代码前就设置 row_factory,并避免在中途对同一连接混用两种结果形状。

重复列名和空结果要单独处理

Row 能按列名读取,但它不能替调用方解决重复列名。联表查询如果同时选择两个同名字段,访问某个重复键时应先给列起明确别名;否则读者看到的键与实际业务含义并不匹配。

# 联表时主动区分两个 id,避免 Row 的列名含义不清。
row = con.execute(
    "SELECT u.user_id AS user_id, o.order_id AS order_id "
    "FROM user_profile AS u JOIN orders AS o ON o.user_id = u.user_id "
    "WHERE u.user_id = ?",
    (7,),
).fetchone()

if row is None:
    # 没有匹配行时不能直接写 row["order_id"]。
    print("没有订单")
else:
    print(row["user_id"], row["order_id"])

如果业务需要字典序列化,可以在边界处显式转换,例如用 {key: row[key] for key in row.keys()} 生成普通字典。不要为了方便把所有查询都改成自定义字典工厂:字典更适合需要 JSON 化的边界,Row 更适合保留轻量的按名、按索引读取能力。

sqlite3.Row 按列名读取时的别名、空结果和游标边界
图2:列名访问边界说明图,展示别名、空结果、已有游标和 Row 键之间的关系,不是运行证据。

迁移时可以按这张表快速判断

场景建议原因
字段位置长期固定保留元组下标简单且无额外命名约定
查询字段会调整使用 sqlite3.Row按列名表达业务含义
联表有同名字段用 AS 起别名避免键名含义不清
fetchone() 可能为空先判断 None避免对空结果按键读取
需要输出 JSON边界处转 dict不要让内部结果形状扩散到所有层

最小核对顺序是:连接设置是否早于游标创建;SQL 是否给表达式和联表字段命名;读取前是否处理 None;循环读取时是否只使用当前查询返回的键。这样就能把“按列名读取”从一处技巧变成稳定的数据访问约定。

sqlite3.Row 能不能像字典一样调用 get?

不要把它当作完整字典使用。按列名读取用下标语法,列名清单用 keys();如果确实需要 get() 等字典行为,在边界处显式转换为字典。

可以只给一个游标设置 row_factory 吗?

可以,游标的 row_factory 也能单独设置。但连接级设置更容易保持多个游标的一致性;只影响单个查询时才考虑游标级覆盖。

列名访问大小写不敏感,还要统一别名吗?

要。大小写不敏感解决的是访问便利,不解决联表重复字段、表达式名称不稳定和团队阅读问题。统一别名仍然是更清晰的接口约定。

Python sqlite3.Row 的关键不是把查询结果“变成字典”,而是为同一行保留轻量的列名映射。把设置放在连接初始化阶段,用明确的 SQL 别名,并在读取前处理空结果,基本就能覆盖按列名读取时最常见的迁移问题。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>