☰
python使用cursor.execute()执行sql插入语句时没有打印异常信息也没有真正插入到表:用TaoToken统一Key排查事务提交与异常捕获
2026/10/8 6:15:52 网站建设 项目流程

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 配置。有些驱动支持在连接时传autocommit=True,有些需要调用connection.autocommit(True),还有些(比如 pymysql)是autocommit=False默认。如果你以为开了 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_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api (这个不加 UTM)。

你需要先拿到自己的 Key,然后配置到环境变量里。这一步不是注册教程,而是告诉你 Key 在排查中的用法:当你的插入操作是通过统一通道触发的,你可以用同一个 Key 去查调用日志,看这次请求的入参、出参、耗时、状态码。如果日志显示请求成功返回,但数据库没数据,那问题 100% 在数据库事务层;如果日志显示请求根本没到,那问题在调用链路。

具体操作:进入控制台的 API Keys 页面创建或复制 Key,地址是 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。拿到 Key 后,建议用环境变量管理,不要硬编码:

export TAOTOKEN_API_KEY="sk-你的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_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,这两个地方能帮你确认通道配置是否正确。

如果你做的是长期编码或 Agent 类任务,可以考虑 Coding Plan,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,它适合需要持续调用、频繁写库的场景。Claude Code 相关的接入在 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite 。

这里要强调一点: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)

这一节是核心,直接给你能跑的代码。我会用三种常见驱动举例:pymysql(MySQL)、cx_Oracle(Oracle)、psycopg2(PostgreSQL),因为它们的 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", encoding="utf-8") as f: cfg = json.load(f)["mysql"] connection = pymysql.connect( host=cfg["host"], port=cfg["port"], user=cfg["user"], password=cfg["password"], database=cfg["database"], charset=cfg["charset"], autocommit=cfg["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()返回的是受影响行数,不是None(pymysql 会返回行数)。如果你看到返回 1,说明 SQL 执行成功,但没 commit 就不会落库。connection.commit()之后才会真正写入。

3.2 cx_Oracle 完整写法

import json import cx_Oracle with open("config/db_config.json", "r", encoding="utf-8") as f: cfg = json.load(f)["oracle"] connection = cx_Oracle.connect( user=cfg["user"], password=cfg["password"], dsn=cfg["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", encoding="utf-8") as f: cfg = json.load(f)["postgres"] connection = psycopg2.connect( host=cfg["host"], port=cfg["port"], user=cfg["user"], password=cfg["password"], dbname=cfg["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": f"Bearer {api_key}"} resp = requests.post( "https://taotoken.net/api", json=payload, headers=headers, timeout=10, ) return resp.status_code, resp.text

这样每次插入前后都能在 TaoToken 控制台看到调用记录,方便核对请求是否到达、参数是否正确。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的 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_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,你可以用它测试通道是否正常。API Keys 管理在 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。

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 用 autocommit=True 做对照实验

为了确认问题出在事务提交上,你可以做一个对照实验:把 autocommit 改成 True,再跑一次。

connection = pymysql.connect( host=cfg["host"], port=cfg["port"], user=cfg["user"], password=cfg["password"], database=cfg["database"], charset=cfg["charset"], autocommit=True, )

如果 autocommit=True 时数据能落库,autocommit=False 时不能,那就 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": f"Bearer {api_key}"}

确认api_key是从环境变量读的,不是空字符串。如果 Key 过期了,去 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 重新生成。

5.2 local proxy failed

这个报错通常出现在网络层,说明请求没发出去。检查你的网络配置,确认 API 地址是 https://taotoken.net/api 。如果你在公司内网,可能需要配置代理白名单。注意,这里说的是正常的网络配置,不是让你去搞什么特殊通道。

5.3 reading choices 报错

如果你在解析模型返回时看到reading 'choices'相关的错误,说明返回结构和你预期的不一样。可能是请求参数错了,或者通道返回了错误信息。打印完整返回内容:

resp = requests.post(url, json=payload, headers=headers) 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_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite ,里面有完整的配置说明。

5.5 数据库层的常见报错

除了 API 层,数据库层也有几个高频报错:

报错一:pymysql.err.IntegrityError: (1062, "Duplicate entry '113' for key 'PRIMARY'")

这是主键冲突,说明 stu_id=113 已经存在。解决方法:换一个 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, "Can't 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/api
  • Key: 从控制台复制的 API Key
  • Model ID: 从模型列表页选的模型标识

任何一项不对,都会导致调用失败。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有每个工具的详细配置。

排查方法给完了,最后一节给你 CTA 分流。

6. 按场景选择入口:排障、验证模型、长期编码

根据你当前的需求,选择对应的入口。

如果你在排查接入问题、401、local proxy failed 这类报错,去 API Keys 页面和接入文档:

  • API Keys: https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite
  • 接入文档: https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite

如果你要验证模型是否正常、测试通道连通性,去模型对话页面:

  • 模型对话: https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite

如果你做的是长期编码、Agent 类任务,需要持续调用和频繁写库,去 Coding Plan:

  • Coding Plan: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite

Claude Code 用户看这里:

  • Claude Code 接入: https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite

回到你的原始问题:cursor.execute()执行 INSERT 没异常也没落库,先检查connection.commit()有没有调用,再检查 autocommit 配置,最后检查 except 块有没有吞掉异常。这三步走完,问题基本就定位了。TaoToken 的统一 Key 和调用日志,帮你在调用链路上游快速排除「请求没发出去」的可能性,把精力集中在数据库事务层。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询