写入失败大概率是strict_trans_tables或strict_all_tables等sql_mode项启用所致;需执行select @@sql_mode;比对报错字段与值,再查表结构验证,而非盲目修改配置。

直接结论:写入失败大概率是 STRICT_TRANS_TABLES 或 STRICT_ALL_TABLES 在起作用,不是数据库坏了,而是 MySQL 拒绝了不合规的数据 —— 先查 @@sql_mode,再比对报错信息里的字段和值,别急着改配置。
怎么确认是 sql_mode 导致的写入失败
别猜,用 SQL 直接验证。执行:SELECT @@sql_mode;,重点看输出里有没有:STRICT_TRANS_TABLES、STRICT_ALL_TABLES、NO_ZERO_DATE、ERROR_FOR_DIVISION_BY_ZERO 这几项。再结合你收到的具体错误信息:
- 报错
Field 'xxx' doesn't have a default value→ 很可能是STRICT_TRANS_TABLES在拦截缺失值 - 报错
Incorrect datetime value: '0000-00-00'或'2024-02-30'→NO_ZERO_DATE或NO_ZERO_IN_DATE生效 - 报错
Data too long for column 'xxx'(错误号 1406)→ 字符串超长,且启用了严格模式 - 报错
Out of range value for column 'xxx'(错误号 1264)→ 数值超出定义范围,比如向TINYINT插入 300
如果错误信息里明确指向某个字段,就立刻查它的定义:DESCRIBE table_name;,核对类型、长度、是否 NOT NULL、有无 DEFAULT。
为什么 SET SESSION sql_mode='...' 有时没用
因为应用层或中间件可能在连接建立后立刻重设了 sql_mode,覆盖你的会话设置。常见场景包括:
- Java 应用使用 JDBC URL 带了
?sessionVariables=sql_mode=TRADITIONAL - Python 的 PyMySQL 或 mysqlclient 初始化时显式执行
SET sql_mode = ... - ORM 如 Django、Peewee 默认注入
sql_mode='TRADITIONAL' - 云数据库(如阿里云 RDS)禁止修改全局变量,只允许连接时指定
排查方法:SET GLOBAL general_log = 1;,复现一次失败写入,然后查 general_log 文件,搜索 SET sql_mode,看是谁在建连后又改了一次。
临时验证与生产环境的分寸感
开发/测试阶段可以快速验证问题是否出在模式上:
- 当前会话临时降级:
SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION';(注意别写空字符串'',MySQL 8.0+ 会自动 fallback 到默认严格模式) - 导入旧脚本时加参数:
mysql -u user -p --sql-mode="" db_name - 但生产环境切忌长期依赖这种绕过 —— 它掩盖了数据质量或字段设计缺陷
真正要动的,往往是字段本身:ALTER TABLE t MODIFY col VARCHAR(255) NOT NULL DEFAULT '';,或者补全应用层插入时必填的字段,而不是让数据库“将就”。
永久修改必须盯住三个细节
改配置文件不是写完就完事。以下三点漏一个,重启后 @@global.sql_mode 就不会变:
- 必须写在
[mysqld]段下,不能放在[client]或[mysql]里 - 值要用双引号包裹,且逗号后不能有空格:
sql_mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION"(不是"STRICT_TRANS_TABLES, NO_ENGINE_SUBSTITUTION") - 改完必须重启服务:
sudo systemctl restart mysql,然后立刻验证:SELECT @@global.sql_mode;
最麻烦的从来不是怎么写配置,而是你改好了,却不知道哪段初始化代码、哪个连接池、哪条 JDBC URL 正在悄悄把它再改回去 —— 所以永远先查日志,再动手。











