MySQL 預設運行在「寬鬆模式」:數據截斷只警告、非法日期存 '0000-00-00'、字符串塞進整型欄位自動轉 0、**不報錯、不中斷、悄悄存下髒數據**。嚴格模式 (STRICT_TRANS_TABLES / STRICT_ALL_TABLES) 則在 INSERT/UPDATE 遇到數據不合法時**直接報錯回滾**,在源頭攔截髒數據。
[mysqld]
# MySQL 5.7 / 8.0 推薦組合
sql_mode = "STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO"
# 解釋:
# STRICT_TRANS_TABLES : 事務表嚴格模式 (非事務表截斷仍警告,兼容性較好)
# NO_ENGINE_SUBSTITUTION : 指定引擎不可用時不自動降級,直接報錯
# NO_ZERO_DATE : 禁止 '0000-00-00' 日期
# NO_ZERO_IN_DATE : 禁止月/日為 0 的日期 (如 2023-00-01)
# ERROR_FOR_DIVISION_BY_ZERO: 除零報錯而非返回 NULL
修改後 systemctl restart mysqld 生效。
SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO';
-- 或全局臨時 (重啟失效)
SET GLOBAL sql_mode = '...';
SELECT @@sql_mode; -- 當前會話
SELECT @@GLOBAL.sql_mode; -- 全局
| 誤區 | 真相 |
|---|---|
| 「嚴格模式會導致舊系統報錯無法運行」 | 舊系統應修復代碼邏輯,而非降級資料庫。可先在測試環境開啟、修復代碼、再推生產。 |
| 「用 TRADITIONAL 模式最省事」 | TRADITIONAL 包含 STRICT_TRANS_TABLES 等,但過於激進 (含 NO_AUTO_CREATE_USER 等已廢棄項),建議顯式列出需要的模式。 |
| 「非事務表不受影響」 | STRICT_TRANS_TABLES 僅約束事務表;若全表皆為 InnoDB (預設),實際上全覆蓋。若有 MyISAM/MEMORY 表,考慮 STRICT_ALL_TABLES (慎用,影響面大)。 |
| 「ANSI 模式等同嚴格模式」 | ANSI 改變語法行為 (如 || 為字符串拼接),不等於嚴格數據校驗。 |
CREATE TABLE `t_strict` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(10) NOT NULL,
`age` TINYINT UNSIGNED NOT NULL,
`created_at` DATETIME NOT NULL
) ENGINE=InnoDB;
-- 1. 字符串長度超限
INSERT INTO `t_strict` (`name`, `age`, `created_at`) VALUES ('這個名字太長了超過十個字符', 25, NOW());
-- ERROR 1406 (22001): Data too long for column 'name' at row 1
-- 2. 字符串塞入整型
INSERT INTO `t_strict` (`name`, `age`, `created_at`) VALUES ('test', 'abc', NOW());
-- ERROR 1366 (HY000): Incorrect integer value: 'abc' for column 'age'
-- 3. 非法日期
INSERT INTO `t_strict` (`name`, `age`, `created_at`) VALUES ('test', 25, '0000-00-00');
-- ERROR 1292 (22007): Incorrect datetime value: '0000-00-00'
-- 4. 除零
SELECT 1/0; -- ERROR 1365 (22012): Division by 0
嚴格模式不是選項,是專業工程師的底線。 新專案初始化即配置;存量專案制定計劃:測試環境開啟 → 修復代碼/清洗數據 → 預發驗證 → 生產變更。別讓「寬容」成為技術債的溫床。