1. 从一次真实的 DataError 1265 说起
pymysql.err.DataError: (1265, "Data truncated for column 'num' at row 1")这个报错,几乎每个用 Python 往 MySQL 写数据的人都会撞上一次。它的字面意思是「第 1 行的 num 列数据被截断了」,但真正让人抓狂的地方在于:你明明把字段改成了varchar(100),甚至把datetime也换成了varchar,报错依旧纹丝不动。
这个报错的核心场景是:用 pandas 读取 Excel 或 CSV,再通过 pymysql 批量插入 MySQL。数据源里num可能是801001.SI这种带字母和点的字符串,而建表时字段类型写成了int或float,MySQL 在严格模式下拒绝隐式转换,直接抛 1265。另一种常见情况是字段长度不够,比如varchar(32)装不下 50 个字符的字符串。
适合谁看:正在做财经数据入库、爬虫落库、Excel 转数据库的 Python 开发者;已经搜过「Data truncated for column」但改了字段类型还是没解决的;以及想把数据库连接配置统一管理、不想在每个脚本里硬编码 host/password 的人。
我试过最坑的一次,是表已经存在,create table被try/except吞掉,字段类型根本没变,改代码改了半天全是无用功。下面把定位流程拆成可复制的步骤,顺带把数据库连接配置和统一 Key 管理一起理清楚。
2. 定位 1265 之前:先把连接配置和统一 Key 管好
排查数据库报错时,最怕的就是环境变量、密码、host 散落在十几个脚本里,改一个地方漏一个地方。所以在动手查 1265 之前,先把连接配置抽出来。
TaoToken 在这里的角色是统一管理大模型和工具链的 API Key。当你用 AI 辅助排查 SQL 报错、生成建表语句、或者让 coding agent 帮你改 pymysql 脚本时,不需要在每个工具里重复填 Key。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。
具体来说,你可以去控制台创建一个 Key,然后在模型对话里贴上报错堆栈让模型帮你分析字段类型;也可以在 coding plan 里让 agent 直接读你的config.toml和建表语句,定位num列到底被定义成了什么。API Keys 管理页面可以随时轮换 Key,接入文档里有各语言的调用示例。
注意:TaoToken 管的是 AI 能力的 Key,不是数据库密码。数据库的 host/user/password 仍然放在你自己的
config.toml里,两者不要混。
这一步的实际价值:当你后面要反复让 AI 帮你检查sql_mode、字段长度、插入值类型时,不用每次重新配置环境,一个 Key 走通模型对话和 coding agent。
3. 可复制配置:config.toml 骨架与建表修正
3.1 config.toml 骨架
把数据库连接和 TaoToken 配置分开写,避免混在一起:
# config.toml [database] host = "127.0.0.1" port = 3306 user = "root" password = "your_db_password" database = "quant_invest" charset = "utf8mb4" [taotoken] api_base = "https://taotoken.net/api" api_key = "sk-你的TaoTokenKey" default_model = "claude-sonnet"Python 读取:
import tomllib # Python 3.11+ # 低版本用 pip install tomli,然后 import tomli as tomllib with open("config.toml", "rb") as f: cfg = tomllib.load(f) db_cfg = cfg["database"] tt_cfg = cfg["taotoken"]3.2 建表语句的三个致命细节
原始代码里建表语句是这样的:
cursor.execute('create table catering_sale(num varchar primary key,date datetime, sale varchar')这行 SQL 有三个问题:varchar没给长度、datetime和插入的字符串不匹配、最后缺右括号。修正后:
CREATE TABLE IF NOT EXISTS catering_sale ( num VARCHAR(64) NOT NULL, trade_date VARCHAR(32), sale VARCHAR(64), PRIMARY KEY (num) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;关键点:num用VARCHAR(64)而不是INT,因为801001.SI这种值根本塞不进整数类型;日期字段先用VARCHAR(32)接住,后续需要再STR_TO_DATE转换;主键用num时确保数据源里没有重复值。
3.3 插入语句与类型转换
insert_sql = """ INSERT INTO catering_sale (num, trade_date, sale) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE sale = VALUES(sale) """ for r in range(len(data)): num = str(data.iloc[r, 0]) trade_date = str(data.iloc[r, 1]) sale = str(data.iloc[r, 2]) cursor.execute(insert_sql, (num, trade_date, sale))ON DUPLICATE KEY UPDATE可以避免主键重复时直接抛IntegrityError 1062,比每次手动删表省事。
4. 验证请求:复现报错、修正字段、重跑插入
4.1 先复现 1265
在 MySQL 客户端里手动制造一次截断:
CREATE TABLE test_num (num INT PRIMARY KEY); INSERT INTO test_num (num) VALUES ('801001.SI');你会看到:
ERROR 1265 (01000): Data truncated for column 'num' at row 1这说明num列是INT,而插入值是字符串,严格模式下 MySQL 拒绝转换。
4.2 检查当前表的真实字段类型
很多人改了代码但没改表,因为create table被try/except吞了。用这条 SQL 看真实结构:
SHOW CREATE TABLE catering_sale;或者:
SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'catering_sale';如果num显示int,说明你的建表语句根本没生效,表是之前建的。
4.3 检查 sql_mode 是否严格
SELECT @@sql_mode;如果结果里包含STRICT_TRANS_TABLES或STRICT_ALL_TABLES,那么类型不匹配会直接报错而不是警告。你可以临时关闭来验证:
SET SESSION sql_mode = '';但生产环境不建议关,正确做法是改字段类型。
4.4 修正字段并重跑
ALTER TABLE catering_sale MODIFY num VARCHAR(64) NOT NULL; ALTER TABLE catering_sale MODIFY trade_date VARCHAR(32); ALTER TABLE catering_sale MODIFY sale VARCHAR(64);然后重跑 Python 插入脚本。成功时不会有任何输出,用查询验证:
cursor.execute("SELECT COUNT(*) FROM catering_sale") print(cursor.fetchone())如果返回的行数等于 Excel 行数,说明全部入库成功。
4.5 用 TaoToken 辅助定位
把SHOW CREATE TABLE的结果和报错堆栈一起贴到模型对话里,让模型对比字段类型和插入值。或者在 coding plan 里让 agent 直接读你的config.toml和脚本,它会指出num列定义和str(num)之间的类型冲突。接入文档里有完整的 API 调用方式,API Keys 页面可以管理你的 Key。
5. 本篇常见错排查
5.1 改了字段类型还是报 1265
原因:表已存在,create table没执行。try/except捕获了异常但没打印,你以为建表成功了。解决:先DROP TABLE IF EXISTS catering_sale;再建,或者用ALTER TABLE改字段。
5.2 报 1146 Table doesn't exist
原因:建表语句缺右括号或语法错误,表根本没建成功。解决:把 SQL 拿到客户端单独执行,看具体语法错误。
5.3 报 1062 Duplicate entry
原因:主键重复。可能是数据源里有重复值,也可能是上次插入的数据没删。解决:用ON DUPLICATE KEY UPDATE,或者插入前TRUNCATE TABLE。
5.4 varchar 不写长度
varchar不写长度在部分 MySQL 版本里默认长度是 1,插入任何超过 1 个字符的值都会截断。必须写varchar(64)这种明确长度。
5.5 datetime 字段插入字符串
如果字段是datetime,插入'2020-10-11'通常可以,但插入'20201011'或空字符串就会报 1265。排查时先用varchar接住,确认数据格式后再改类型。
5.6 pandas 的 .ix 已废弃
data.ix[r, 0]在新版 pandas 里会报FutureWarning,改用data.iloc[r, 0]。这个不直接影响 1265,但会让你的排查过程多一层干扰。
6. 把 Key 和配置统一后的下一步
数据库报错排查完之后,建议把config.toml纳入版本管理(密码用环境变量覆盖),这样换机器时不用重新翻脚本。TaoToken 的 Key 可以在 API Keys 页面随时轮换,接入文档里有 Python、Node、curl 的示例。如果你后面要让 agent 自动改 SQL、生成建表语句、或者批量修数据管道,可以在 coding plan 里配置长期使用的 Key,避免每次手动贴。
回到 1265 本身:核心就三件事——字段类型对不对、字段长度够不够、表是不是真的按你写的建了。把SHOW CREATE TABLE和SELECT @@sql_mode这两条命令记住,下次再遇到 Data truncated,五分钟内就能定位到根因。