ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

python使用cursor.execute()执行sql插入语句时没有打印异常信息也没有真正插入到表:用TaoToken统一Key排查事务提交与异常捕获

2026/10/8 6:16:07 拓冰建站 浏览量
python使用cursor.execute()执行sql插入语句时没有打印异常信息也没有真正插入到表:用TaoToken统一Key排查事务提交与异常捕获 1. 为什么 cursor.execute() 执行 INSERT 既不报错也不落库你写了一段 Python 连数据库的代码cursor.execute()跑完了控制台干干净净没有异常没有报错你满心欢喜去查表结果数据根本没进去。这个场景在 cx_Oracle、pymysql、psycopg2、sqlite3 里都出现过尤其是刚接触数据库事务的同学第一次遇到会非常懵。核心检索词先摆出来python cursor.execute() 执行 sql 插入语句没有异常也没有真正插入到表这是一个典型的「事务未提交 异常被吞」组合问题。它不是什么玄学而是数据库连接层的事务模型在起作用。先说清楚这件事的本质。绝大多数数据库驱动除了 sqlite3 默认行为略有差异在建立连接后默认是关闭 autocommit的。也就是说你执行的每一条 INSERT、UPDATE、DELETE 都处在一个隐式开启的事务里。这个事务只有在你显式调用connection.commit()之后才会真正写进数据库文件或日志被其他会话看到。如果你不 commit程序正常结束或者连接关闭时这个未提交的事务会被回滚数据自然就没了。那为什么没有异常呢因为从数据库的角度看你的 SQL 语法没问题参数绑定没问题执行也确实成功了——它只是待在事务缓冲区里没提交而已。cursor.execute()的返回值对于非查询语句本来就是None它不会告诉你「我执行了但没提交」。所以你会看到「无异常、无数据」这种诡异组合。再叠加第二个坑异常被静默吞掉。很多人的代码写成这样try: cursor.execute(sql, values) except: pass或者用了宽泛的except Exception但只print了一行甚至print被日志级别过滤掉了。这时候如果 SQL 真的有问题比如字段类型不匹配、唯一约束冲突异常确实抛了但被你自己的代码吃掉了表现和「没执行」一模一样。第三个坑是 autocommit 配置。有些驱动支持在连接时传autocommitTrue有些需要调用connection.autocommit(True)还有些比如 pymysql是autocommitFalse默认。如果你以为开了 autocommit 其实没开或者反过来行为就会和预期不符。这三个角度——事务未 commit、autocommit 关闭、异常被静默吞掉——基本覆盖了 90% 的「INSERT 不落库」问题。下面我会给你一套可复制的连接与游标配置片段、显式 commit 与 rollback 的写法以及用 TaoToken 统一 Key 做调用日志核对的验证动作帮你把真实原因定位出来。适合谁看正在用 Python 操作 MySQL、Oracle、PostgreSQL、SQLite 的开发者被「无异常无数据」折磨过的同学想搞清楚事务提交机制的人。接下来从环境准备开始一步步把这个问题拆开。2. TaoToken 统一 Key 前置准备与调用日志核对思路在排查数据库插入问题时很多人只盯着数据库本身忽略了「调用链路上游」的日志。实际上如果你的 Python 程序是通过某个 API 网关、模型服务或者统一接入层去触发数据库操作的比如 AI Agent 调用工具写库、定时任务通过统一 Key 调用后端那么调用日志能帮你快速判断请求到底有没有到达后端、参数对不对、返回码是什么。TaoToken 在这里的角色是一个统一 Key / API 通道它把多个模型和服务的调用收敛到一个 Key 上方便你做日志核对和问题定位。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 这个不加 UTM。你需要先拿到自己的 Key然后配置到环境变量里。这一步不是注册教程而是告诉你 Key 在排查中的用法当你的插入操作是通过统一通道触发的你可以用同一个 Key 去查调用日志看这次请求的入参、出参、耗时、状态码。如果日志显示请求成功返回但数据库没数据那问题 100% 在数据库事务层如果日志显示请求根本没到那问题在调用链路。具体操作进入控制台的 API Keys 页面创建或复制 Key地址是 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。拿到 Key 后建议用环境变量管理不要硬编码export TAOTOKEN_API_KEYsk-你的key然后在 Python 里读取import os api_key os.environ.get(TAOTOKEN_API_KEY) if not api_key: raise RuntimeError(TAOTOKEN_API_KEY 未设置请检查环境变量)为什么要用统一 Key因为排查问题时最怕的是「不知道请求有没有发出去」。统一 Key 的好处是所有调用都走同一个通道日志集中你可以在一个地方看到完整的调用记录。模型对话入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 这两个地方能帮你确认通道配置是否正确。如果你做的是长期编码或 Agent 类任务可以考虑 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 它适合需要持续调用、频繁写库的场景。Claude Code 相关的接入在 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite 。这里要强调一点TaoToken 是帮你做调用日志核对的工具不是数据库本身。数据库事务提交的问题最终还是要靠connection.commit()解决。但有了统一 Key 的日志你能快速排除「请求没发出去」这个可能性把精力集中在事务层。前置准备清单拿到 TaoToken API Key配置到环境变量确认 API 通道地址是 https://taotoken.net/api打开接入文档核对 Base URL、Key、Model ID 三件套准备好你的数据库连接信息host、port、user、password、database做完这些你就可以开始写可复制的配置片段了。下一节我会给你完整的连接、游标、commit、rollback 代码以及一个 JSON 配置片段路径和原文保持一致。3. 可复制的连接与游标配置片段含 commit/rollback这一节是核心直接给你能跑的代码。我会用三种常见驱动举例pymysqlMySQL、cx_OracleOracle、psycopg2PostgreSQL因为它们的 autocommit 默认行为不同正好覆盖你遇到的各种情况。先看一个通用的配置 JSON放在项目根目录的config/db_config.json{ mysql: { host: 127.0.0.1, port: 3306, user: app_user, password: your_password, database: test_db, charset: utf8mb4, autocommit: false }, oracle: { user: app_user, password: your_password, dsn: 127.0.0.1:1521/ORCLPDB1, autocommit: false }, postgres: { host: 127.0.0.1, port: 5432, user: app_user, password: your_password, dbname: test_db, autocommit: false } }注意autocommit我显式写成false这样你一眼就知道当前连接不是自动提交的必须手动 commit。这是排查问题的关键——把隐式行为变成显式配置。3.1 pymysql 完整写法import json import pymysql with open(config/db_config.json, r, encodingutf-8) as f: cfg json.load(f)[mysql] connection pymysql.connect( hostcfg[host], portcfg[port], usercfg[user], passwordcfg[password], databasecfg[database], charsetcfg[charset], autocommitcfg[autocommit], ) cursor connection.cursor() try: sql INSERT INTO student (stu_id, stu_name) VALUES (%s, %s) values (113, liming) affected cursor.execute(sql, values) print(affected rows:, affected) connection.commit() print(commit done) except Exception as e: connection.rollback() print(Error occurred:, repr(e)) finally: cursor.close() connection.close()关键点cursor.execute()返回的是受影响行数不是Nonepymysql 会返回行数。如果你看到返回 1说明 SQL 执行成功但没 commit 就不会落库。connection.commit()之后才会真正写入。3.2 cx_Oracle 完整写法import json import cx_Oracle with open(config/db_config.json, r, encodingutf-8) as f: cfg json.load(f)[oracle] connection cx_Oracle.connect( usercfg[user], passwordcfg[password], dsncfg[dsn], ) cursor connection.cursor() try: sql INSERT INTO student (stu_id, stu_name) VALUES (:value1, :value2) values {value1: 113, value2: liming} var1 cursor.execute(sql, values) print(execute return:, var1) connection.commit() print(commit done) except Exception as e: connection.rollback() print(Error occurred:, repr(e)) finally: cursor.close() connection.close()cx_Oracle 的cursor.execute()对非查询语句返回None这是正常的不要以为返回 None 就是没执行。真正的落库动作在connection.commit()。3.3 psycopg2 完整写法import json import psycopg2 with open(config/db_config.json, r, encodingutf-8) as f: cfg json.load(f)[postgres] connection psycopg2.connect( hostcfg[host], portcfg[port], usercfg[user], passwordcfg[password], dbnamecfg[dbname], ) connection.autocommit cfg[autocommit] cursor connection.cursor() try: sql INSERT INTO student (stu_id, stu_name) VALUES (%s, %s) values (113, liming) cursor.execute(sql, values) connection.commit() print(commit done) except Exception as e: connection.rollback() print(Error occurred:, repr(e)) finally: cursor.close() connection.close()psycopg2 默认也是不自动提交的connection.autocommit False是默认值显式写出来更清楚。3.4 用上下文管理器避免忘记 commit如果你经常忘记 commit可以用with语句。pymysql 和 psycopg2 都支持with connection.cursor() as cursor: cursor.execute(sql, values) connection.commit()但注意with connection.cursor()只管理游标的关闭不管理事务提交。你还是要手动 commit。有些 ORM 框架比如 SQLAlchemy会在with session.begin()里自动 commit但原生驱动不会。3.5 把 TaoToken 调用日志接进来如果你的插入操作是通过统一通道触发的可以在代码里加一段日志上报import os import requests def report_call_log(payload): api_key os.environ.get(TAOTOKEN_API_KEY) headers {Authorization: fBearer {api_key}} resp requests.post( https://taotoken.net/api, jsonpayload, headersheaders, timeout10, ) return resp.status_code, resp.text这样每次插入前后都能在 TaoToken 控制台看到调用记录方便核对请求是否到达、参数是否正确。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的 Base URL、Key、Model ID 三件套说明。配置片段给完了下一节我们验证请求看成功结果长什么样。4. 验证请求与成功结果从 execute 到落库的完整链路写完配置你需要验证两件事一是cursor.execute()确实执行了二是connection.commit()之后数据真的落库了。这一节给你完整的验证步骤和预期输出。4.1 验证 execute 是否执行在 execute 之后立刻打印受影响行数和游标状态affected cursor.execute(sql, values) print(affected rows:, affected) print(rowcount:, cursor.rowcount)pymysql 的cursor.rowcount会返回受影响行数INSERT 成功通常是 1。cx_Oracle 的cursor.rowcount也会返回 1。如果你看到 0说明 SQL 执行了但没匹配到任何行对 INSERT 来说可能是触发器或约束导致这时候要检查表结构和约束。4.2 验证 commit 是否生效commit 之后用另一个连接去查check_conn pymysql.connect(**cfg) check_cursor check_conn.cursor() check_cursor.execute(SELECT stu_id, stu_name FROM student WHERE stu_id %s, (113,)) print(found:, check_cursor.fetchall()) check_cursor.close() check_conn.close()如果这里能查到数据说明 commit 生效了。如果查不到说明 commit 没执行或者被回滚了。4.3 用 TaoToken 日志核对调用链路如果你的插入是通过统一通道触发的打开 TaoToken 控制台的调用日志页面找到对应的请求记录。重点看三个字段请求时间、状态码、返回内容。状态码 200 且返回内容正常说明请求到达了后端如果状态码 4xx/5xx说明请求本身有问题还没到数据库层。模型对话入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 你可以用它测试通道是否正常。API Keys 管理在 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。4.4 成功结果的预期输出一个正常的执行流程控制台应该输出affected rows: 1 rowcount: 1 commit done found: ((113, liming),)如果你看到affected rows: 1但found: ()说明 execute 成功但 commit 没生效或者 commit 之后被回滚了。如果你看到affected rows: 0说明 SQL 没匹配到行检查 WHERE 条件或表约束。4.5 用 autocommitTrue 做对照实验为了确认问题出在事务提交上你可以做一个对照实验把 autocommit 改成 True再跑一次。connection pymysql.connect( hostcfg[host], portcfg[port], usercfg[user], passwordcfg[password], databasecfg[database], charsetcfg[charset], autocommitTrue, )如果 autocommitTrue 时数据能落库autocommitFalse 时不能那就 100% 确认是 commit 的问题。这个对照实验能帮你快速定位不用瞎猜。4.6 检查数据库隔离级别有些数据库的隔离级别会影响你能否看到未提交的数据。比如 MySQL 的 REPEATABLE READ 级别下另一个连接在事务提交前看不到新数据。你可以查一下当前隔离级别cursor.execute(SELECT transaction_isolation) print(cursor.fetchone())如果隔离级别是 REPEATABLE READ而你的查询连接在一个长事务里可能看不到刚提交的数据。这时候要么提交查询连接的事务要么换一个连接查。验证步骤给完了下一节我们看常见报错和排查方法。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节对照真实报错帮你快速定位。每个报错我都给出原因和解决方法。5.1 401 Unauthorized如果你在调用 TaoToken API 时看到 401说明 Key 不对或没传。检查headers {Authorization: fBearer {api_key}}确认api_key是从环境变量读的不是空字符串。如果 Key 过期了去 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 重新生成。5.2 local proxy failed这个报错通常出现在网络层说明请求没发出去。检查你的网络配置确认 API 地址是 https://taotoken.net/api 。如果你在公司内网可能需要配置代理白名单。注意这里说的是正常的网络配置不是让你去搞什么特殊通道。5.3 reading choices 报错如果你在解析模型返回时看到reading choices相关的错误说明返回结构和你预期的不一样。可能是请求参数错了或者通道返回了错误信息。打印完整返回内容resp requests.post(url, jsonpayload, headersheaders) print(resp.status_code) print(resp.text)看resp.text里的实际内容通常会有明确的错误提示。5.4 OAuth 相关报错如果你用的是 Claude Code 或其他需要 OAuth 的工具报错可能是 token 过期或 scope 不对。检查你的 OAuth 配置确认回调地址和权限范围。Claude Code 接入文档在 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite 里面有完整的配置说明。5.5 数据库层的常见报错除了 API 层数据库层也有几个高频报错报错一pymysql.err.IntegrityError: (1062, Duplicate entry 113 for key PRIMARY)这是主键冲突说明 stu_id113 已经存在。解决方法换一个 ID或者用INSERT ... ON DUPLICATE KEY UPDATE。报错二cx_Oracle.DatabaseError: ORA-00942: table or view does not exist表不存在检查表名大小写和 schema。Oracle 默认大写如果你建表时用了小写查询时要加双引号。报错三psycopg2.errors.UndefinedColumn: column stu_name does not exist字段名不对检查表结构。PostgreSQL 对大小写敏感未加引号的标识符会转成小写。报错四pymysql.err.OperationalError: (2003, Cant connect to MySQL server on 127.0.0.1)连不上数据库检查 host、port、防火墙。确认数据库服务在跑。5.6 异常被静默吞掉的排查如果你怀疑异常被吞了把 except 块改成打印完整堆栈import traceback try: cursor.execute(sql, values) connection.commit() except Exception as e: connection.rollback() traceback.print_exc() print(Error occurred:, repr(e))traceback.print_exc()会打印完整调用栈比print(str(e))信息多得多。如果你之前用的是except: pass赶紧改掉这是排查问题的最大障碍。5.7 三件套核对清单如果你用的是 Cline MCP、Codex auth.json 或 CC Switch确认以下三件套配置正确Base URL: https://taotoken.net/apiKey: 从控制台复制的 API KeyModel ID: 从模型列表页选的模型标识任何一项不对都会导致调用失败。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有每个工具的详细配置。排查方法给完了最后一节给你 CTA 分流。6. 按场景选择入口排障、验证模型、长期编码根据你当前的需求选择对应的入口。如果你在排查接入问题、401、local proxy failed 这类报错去 API Keys 页面和接入文档API Keys: https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档: https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果你要验证模型是否正常、测试通道连通性去模型对话页面模型对话: https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite如果你做的是长期编码、Agent 类任务需要持续调用和频繁写库去 Coding PlanCoding Plan: https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewriteClaude Code 用户看这里Claude Code 接入: https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite回到你的原始问题cursor.execute()执行 INSERT 没异常也没落库先检查connection.commit()有没有调用再检查 autocommit 配置最后检查 except 块有没有吞掉异常。这三步走完问题基本就定位了。TaoToken 的统一 Key 和调用日志帮你在调用链路上游快速排除「请求没发出去」的可能性把精力集中在数据库事务层。