如何设置数据库严格SQL模式_sql_mode参数与数据校验
MySQL 8.0+ 默认严格模式为 STRICT_TRANS_TABLES,仅对新连接生效且受全局/会话两级配置影响;改sql_mode未生效多因仅修改会话级、连接池复用旧连接或配置文件优先级更高需重启。MySQL 8.0+ 默认 strict mode 是什么,为什么你改了 sql_mode 还没生效MySQL 8.0 开始默认启用严格模式(STRICT_TRANS_TABLES),但这个“默认”只作用于新连接、且受全局与会话两级配置影响。你执行 SET sql_mode = 'STRICT_TRANS_TABLES' 后插入脏数据仍成功,大概率是改了会话级却没动全局级,或者应用连接池复用了旧连接。SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES' 才影响后续新连接;已有连接仍用旧值MySQL 配置文件(my.cnf 或 my.ini)里的 sql_mode 设置优先级高于启动后动态 SET,但需重启生效Docker 容器或云数据库(如阿里云 RDS)可能锁定全局 sql_mode,只能通过会话 SET,且每次连接都要重设如何安全地启用完整严格校验:STRICT_TRANS_TABLES vs STRICT_ALL_TABLES两者都开启“拒绝非法值”,但行为关键不同:STRICT_TRANS_TABLES 只对事务表(InnoDB)强制校验,STRICT_ALL_TABLES 对所有表(包括 MyISAM)都生效——但 MyISAM 不支持事务回滚,出错时已写入的部分无法撤回,反而导致数据不一致。生产环境只用 STRICT_TRANS_TABLES,别加 STRICT_ALL_TABLES真正需要强校验时,补上 NO_ZERO_DATE、NO_ZERO_IN_DATE、ERROR_FOR_DIVISION_BY_ZERO,防止日期/除零静默转成默认值避免包含 ALLOW_INVALID_DATES —— 它会让 '2023-02-30' 这种错日期被接受推荐组合:STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZEROPHP/Laravel 连接 MySQL 时 sql_mode 被悄悄覆盖怎么办很多 ORM 或 PDO 驱动会在连接建立后自动执行 SET sql_mode = '' 或覆盖为你指定的值,尤其 Laravel 的 mysql 配置里 'strict' => true 实际发的是 SET sql_mode = 'STRICT_TRANS_TABLES',但若 DBA 在服务器端锁死配置,这条语句会被忽略且无报错。检查 PHP 连接后的实际生效值:SELECT @@sql_mode,别只信配置文件Laravel 中在 config/database.php 的 mysql 配置下加 'options' => [PDO::MYSQL_ATTR_INIT_COMMAND => "SET sql_mode=(SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES',''))"] 这类写法很危险,会主动关掉严格模式更稳妥的做法:在 MySQL 侧固化配置(my.cnf + 重启),让所有客户端统一遵守,而非各框架各自 SETINSERT IGNORE / ON DUPLICATE KEY UPDATE 在 strict mode 下的行为变化非 strict 模式下,INSERT IGNORE 遇到主键冲突或数据截断会静默跳过;strict 模式下,**数据截断会直接报错(Truncated incorrect DOUBLE value 类错误),而主键冲突仍被 IGNORE**——这容易让人误以为“strict 没起作用”。想让重复键也报错?别用 IGNORE,改用 INSERT ... ON DUPLICATE KEY UPDATE 并确保 UPDATE 子句里没有非法值批量插入时,strict mode 下一行失败会导致整批回滚(事务表),而非只跳过那行测试时用 SHOW WARNINGS 查看是否出现 Data truncated 提示,它在 strict 下会升级为 ERROR真正麻烦的不是设不设 strict,而是团队里有人写 SQL 依赖隐式转换(比如把字符串 'abc' 插进 INT 字段),这种代码在 strict 下必挂,但问题根源不在参数,而在数据契约本身没定义清楚。
