十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PyMySQL 里 fetchall 后 fetchone 取空?让走 TaoToken 的 Codex 对照游标位置

PyMySQL 里 fetchall 后 fetchone 取空?让走 TaoToken 的 Codex 对照游标位置 1. 先复现fetchall 之后 fetchone 为什么是 None你写了一段查询execute之后先fetchall()拿到全部结果接着想再取一行做校验结果fetchone()返回Nonefetchmany(3)返回空元组()。这不是 PyMySQL 的 bug而是游标Cursor的取值位置在起作用。PyMySQL 的Cursor对象内部维护一个结果集指针fetchone()、fetchmany(size)、fetchall()三个方法共享同一个指针位置。每次取值都会把指针往后推fetchall()会把指针推到结果集末尾之后再调用fetchone()指针已经在末尾之外自然返回Nonefetchmany(3)同理返回空元组。这个现象在原文 3.5 节只写了「注意」和一段示例输出没有给排查路径。实际项目里它常常和另外几个动作混在一起让人误以为是连接断了或者事务提前结束查询中途调用了ping(reconnectTrue)连接被重建游标失效在只读查询后误调了commit()或rollback()事务边界变化用了SSCursor不缓存游标结果集是流式读取的指针行为更敏感。这篇按排障视角走一遍先把出问题的调用顺序和实际输出整理好再让走 TaoToken 通道的 Codex 对照 PyMySQL 的 Cursor / SSCursor 差异检查execute、fetchall、fetchmany、fetchone的调用顺序判断是重开游标还是换取值方法最后在本地跑同一段查询确认返回值不再为空。适合谁看正在用 PyMySQL 做数据查询、被游标取值顺序坑过、想搞清楚缓存游标和不缓存游标差异的 Python 开发者。核心检索词就是 PyMySQL、fetchall、fetchone、游标位置、SSCursor。2. 前置注册 TaoToken 并创建 Key排障过程中需要让 Codex 帮你对照 PyMySQL 的游标实现和调用顺序这里走 TaoToken 通道。先打开官网注册账号https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册完成后进入控制台创建 API Keyhttps://taotoken.net/console创建 Key 的入口在 API Keys 页面https://taotoken.net/api-keys拿到 Key 之后把 Codex 的 Base URL 填成https://taotoken.net/api注意两点不带/v1不加 UTM 参数。Key 就填刚创建的那把。配置完成后Codex 的请求会走 TaoToken 通道你可以直接让它读你贴进去的 PyMySQL 调用代码和输出对照游标位置做分析。如果你后面要长期做编码和 Agent 类任务可以了解 Coding Planhttps://taotoken.net/coding-plan需要验证模型对话效果用模型对话入口https://taotoken.net/model-chat接入文档在这里https://taotoken.net/doc3. 可复制配置把出问题的调用和输出整理成可分析片段排障第一步不是改代码而是把现场固定下来。你需要准备三段内容连接与游标创建、execute 与 fetch 调用顺序、实际输出。下面是一段最小可复现脚本你可以直接复制到本地跑。import pymysql conn pymysql.connect( host127.0.0.1, userroot, passwordyour_password, databasetestdb, charsetutf8mb4, cursorclasspymysql.cursors.Cursor, # 默认缓存游标 ) try: with conn.cursor() as cursor: sql SELECT id, name FROM code ORDER BY id affected cursor.execute(sql) print(execute 返回受影响行数:, affected) all_rows cursor.fetchall() print(fetchall 结果:, all_rows) one_row cursor.fetchone() print(fetchall 之后 fetchone:, one_row) many_rows cursor.fetchmany(3) print(fetchall 之后 fetchmany(3):, many_rows) finally: conn.close()跑出来的输出大概率是这样execute 返回受影响行数: 5 fetchall 结果: ((1, A), (2, B), (3, C), (4, D), (5, E)) fetchall 之后 fetchone: None fetchall 之后 fetchmany(3): ()把这段脚本和输出一起贴给走 TaoToken 通道的 Codex让它对照 PyMySQL 的Cursor与SSCursor实现检查调用顺序。你可以这样提问这是 PyMySQL 的查询代码和实际输出。execute 返回 5 行 fetchall 拿到全部结果后fetchone 返回 Nonefetchmany(3) 返回空元组。 请对照 pymysql.cursors.Cursor 和 SSCursor 的取值实现 说明游标指针在 fetchall 之后的位置并判断应该重开游标还是换取值方法。 同时检查 ping(reconnectTrue)、commit()、rollback() 是否会让结果提前结束。Codex 会指出Cursor的fetchall()内部循环调用fetchone()直到返回None指针已经到末尾SSCursor不缓存结果fetchall()同样会耗尽流。两者在「取值位置共享」这一点上一致区别在于内存占用和读取时机。4. 验证请求改完在本地跑同一段查询知道原因后改法有两种取决于你的业务需求。第一种需要多次遍历同一结果集就重开游标或重新执行查询。注意execute会重置游标位置但会重新发一次 SQL。with conn.cursor() as cursor: cursor.execute(SELECT id, name FROM code ORDER BY id) all_rows cursor.fetchall() print(第一次全量:, all_rows) # 需要再取一行重新执行 cursor.execute(SELECT id, name FROM code ORDER BY id) first_row cursor.fetchone() print(重开后的第一行:, first_row)第二种本来就想分页取值那就不要先fetchall()直接用fetchone()或fetchmany(size)按顺序取。with conn.cursor() as cursor: cursor.execute(SELECT id, name FROM code ORDER BY id) first cursor.fetchone() print(第一行:, first) next_three cursor.fetchmany(3) print(接下来三行:, next_three) rest cursor.fetchall() print(剩余全部:, rest)输出会按指针顺序推进第一行: (1, A) 接下来三行: ((2, B), (3, C), (4, D)) 剩余全部: ((5, E),)如果你用的是SSCursor取值方式一样但要注意不缓存游标在结果集很大时省内存可一旦fetchall()把流读完后面同样取不到。验证时把cursorclass换成pymysql.cursors.SSCursor再跑一遍确认行为一致。关于ping(reconnectTrue)它用于检查连接是否存活连接关闭时会重连。重连后旧游标失效如果你在查询中途调用可能让结果提前结束。排查时把它从查询逻辑里挪出去只在获取连接后、执行查询前做一次健康检查。关于commit()和rollback()它们结束当前事务。只读查询本身不依赖提交但如果你在fetch之间调用了它们事务边界变化可能影响游标状态。排查时先把这两个调用从查询段里去掉确认返回值正常后再决定放回哪里。改完在本地跑同一段查询确认fetchone()不再返回None、fetchmany(3)不再返回空元组就说明游标位置问题解决了。5. 本篇常见错排查5.1 fetchall 之后 fetchone 返回 None这是最典型的游标位置问题。fetchall()把指针推到末尾后续取值自然为空。判断方法看调用顺序里fetchall()是否在fetchone()之前。解决重开游标重新execute或调整取值顺序先fetchone再fetchall。5.2 fetchmany(3) 返回空元组和上面同源。如果fetchmany(3)之前已经fetchall()指针在末尾返回()。如果之前只取了部分行fetchmany(3)会从当前位置继续取剩余不足 3 行时返回实际剩余行数不会报错。5.3 SSCursor 下取值行为异常SSCursor不缓存结果适合大结果集。但它对连接状态更敏感连接中断、ping(reconnectTrue)触发重连、事务提前结束都可能让流式读取中断。排查时先用默认Cursor复现确认是游标位置问题还是连接问题再切回SSCursor。5.4 ping(reconnectTrue) 让结果提前结束ping本身是健康检查但reconnectTrue时如果连接已关闭会重建连接。重建后旧游标不可用继续fetch会拿不到数据。排查方法把ping调用注释掉看结果是否恢复正常。如果恢复说明是重连导致把ping移到查询开始之前。5.5 commit / rollback 影响查询结果只读查询通常不需要commit。如果你在fetch之间调用了commit()或rollback()事务结束可能让游标状态变化。排查时先移除这两个调用确认查询正常后再按业务需要放回。写操作才需要commit回滚用于异常恢复。5.6 execute 返回值被误当成结果行数execute()返回的是受影响行数不是查询结果。查询结果要通过fetchone、fetchmany、fetchall获取。如果你把execute的返回值当成数据会得到行数而不是行内容。排查时打印execute返回值和fetch返回值对照类型。6. 接入与排障入口排障和接入相关的操作走 API Keys 和接入文档https://taotoken.net/api-keys https://taotoken.net/doc需要验证模型对话效果用模型对话入口https://taotoken.net/model-chat长期做编码和 Agent 类任务看 Coding Planhttps://taotoken.net/coding-planClaudeCodeAnthropic 相关入口https://taotoken.net/claude-code-anthropic回到这次排障本身PyMySQL 的fetchall、fetchmany、fetchone共享同一个游标位置这是设计行为不是故障。排查时先把调用顺序和实际输出固定下来再让走 TaoToken 通道的 Codex 对照Cursor与SSCursor的取值实现判断是重开游标还是换取值方法顺带检查ping(reconnectTrue)、commit()、rollback()是否让结果提前结束。改完在本地跑同一段查询确认返回值不再为空问题就闭环了。
返回列表