MySQL sql_mode從入門(mén)到精通
引言
在MySQL的使用過(guò)程中,你是否遇到過(guò)這些令人困惑的問(wèn)題?
- 同樣的SQL,在測(cè)試環(huán)境能運(yùn)行,上線卻報(bào)錯(cuò)?
- 插入的數(shù)據(jù)被自動(dòng)截?cái)啵瑢?dǎo)致業(yè)務(wù)邏輯出錯(cuò)?
- 分組查詢的結(jié)果時(shí)而正確時(shí)而錯(cuò)誤?
- 遷移Oracle數(shù)據(jù)時(shí),||連接符突然不好使了?
這些問(wèn)題的幕后推手,往往就是 sql_mode——MySQL中一個(gè)至關(guān)重要卻又容易被忽視的配置。
本文將深入淺出地講解sql_mode的核心概念、配置方法、常用模式及其實(shí)際應(yīng)用場(chǎng)景。
1. sql_mode 核心概念
1.1 什么是 sql_mode?
sql_mode是MySQL中語(yǔ)法校驗(yàn)、數(shù)據(jù)校驗(yàn)、行為兼容的核心配置。它定義了:
- SQL語(yǔ)法解析規(guī)則:支持哪些語(yǔ)法特性
- 數(shù)據(jù)有效性校驗(yàn)標(biāo)準(zhǔn):哪些數(shù)據(jù)是合法的
- 與其他數(shù)據(jù)庫(kù)的兼容策略:如何模擬Oracle、SQL Server的行為

1.2 核心作用
| 作用 | 說(shuō)明 | 舉例 |
|---|---|---|
| 規(guī)范SQL語(yǔ)法 | 限制或支持特定的SQL語(yǔ)法 | ONLY_FULL_GROUP_BY要求分組字段明確 |
| 數(shù)據(jù)有效性校驗(yàn) | 阻止無(wú)效數(shù)據(jù)插入/更新 | NO_ZERO_DATE禁止全零日期 |
| 兼容其他數(shù)據(jù)庫(kù) | 模擬其他數(shù)據(jù)庫(kù)的行為 | PIPES_AS_CONCAT啟用` |
| 避免歧義行為 | 明確SQL執(zhí)行邏輯 | 嚴(yán)格模式阻止數(shù)據(jù)截?cái)?/td> |
1.3 通俗理解
寬松模式:像一位隨和的老師,學(xué)生作業(yè)寫(xiě)錯(cuò)了,他只會(huì)提醒一下,還是讓你通過(guò)(產(chǎn)生警告,數(shù)據(jù)被截?cái)嗷蛐拚?/p>
嚴(yán)格模式:像一位嚴(yán)格的考官,一旦發(fā)現(xiàn)錯(cuò)誤,直接判零分(報(bào)錯(cuò),拒絕執(zhí)行)。
2. 查看當(dāng)前 sql_mode
2.1 查看會(huì)話級(jí)(當(dāng)前連接生效)
-- 查看當(dāng)前會(huì)話的sql_mode SELECT @@SESSION.sql_mode; -- 或簡(jiǎn)寫(xiě) SELECT @@sql_mode;
2.2 查看全局級(jí)(所有新連接生效)
-- 查看全局sql_mode SELECT @@GLOBAL.sql_mode;
2.3 不同版本的默認(rèn)值
| MySQL版本 | 默認(rèn)sql_mode |
|---|---|
| MySQL 5.6及以下 | 空字符串(寬松模式) |
| MySQL 5.7 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
| MySQL 8.0+ | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
3. 修改 sql_mode 的三種方式
3.1 會(huì)話級(jí)修改(臨時(shí)生效)
-- 設(shè)置會(huì)話級(jí)sql_mode SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE'; -- 或使用SET命令的簡(jiǎn)寫(xiě) SET @@SESSION.sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES'; -- 在當(dāng)前會(huì)話基礎(chǔ)上追加模式 SET SESSION sql_mode = CONCAT(@@SESSION.sql_mode, ',PIPES_AS_CONCAT'); -- 在當(dāng)前會(huì)話基礎(chǔ)上移除某個(gè)模式 SET SESSION sql_mode = REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', '');
特點(diǎn):僅對(duì)當(dāng)前連接有效,斷開(kāi)連接或重啟后失效,適合臨時(shí)測(cè)試。
3.2 全局級(jí)修改(重啟后失效)
-- 設(shè)置全局sql_mode SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES'; -- 刷新權(quán)限(使新連接立即生效) FLUSH PRIVILEGES;
特點(diǎn):對(duì)所有新建立的連接生效,但MySQL重啟后失效(除非寫(xiě)入配置文件)。
3.3 配置文件修改(永久生效)
# Linux/Mac: /etc/my.cnf 或 /etc/mysql/my.cnf # Windows: MySQL安裝目錄下的 my.ini [mysqld] # 生產(chǎn)環(huán)境推薦配置 sql_mode = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION" # 如果要兼容Oracle # sql_mode = "ANSI,PIPES_AS_CONCAT" # 如果要完全嚴(yán)格(包括非事務(wù)表) # sql_mode = "TRADITIONAL"
修改后重啟MySQL:
# Linux systemctl restart mysqld # 或 service mysql restart # Windows(管理員CMD) net stop mysql net start mysql
4. 常見(jiàn) sql_mode 詳解
4.1 嚴(yán)格模式相關(guān)(核心推薦)
嚴(yán)格模式是數(shù)據(jù)校驗(yàn)的核心,阻止無(wú)效數(shù)據(jù)寫(xiě)入,避免臟數(shù)據(jù)。
| 模式值 | 作用說(shuō)明 |
|---|---|
| STRICT_TRANS_TABLES | 對(duì)事務(wù)表(如InnoDB)啟用嚴(yán)格模式:無(wú)效數(shù)據(jù)插入/更新直接報(bào)錯(cuò);非事務(wù)表(如MyISAM)寬松(僅警告,數(shù)據(jù)截?cái)啵?/td> |
| STRICT_ALL_TABLES | 對(duì)所有表(事務(wù)/非事務(wù))啟用嚴(yán)格模式:無(wú)效數(shù)據(jù)均報(bào)錯(cuò) |
示例:嚴(yán)格模式 vs 寬松模式
-- 創(chuàng)建測(cè)試表
CREATE TABLE test_strict (
id INT,
name VARCHAR(5) -- 姓名最長(zhǎng)5個(gè)字符
) ENGINE=InnoDB;寬松模式(未啟用STRICT_TRANS_TABLES):
INSERT INTO test_strict VALUES (1, 'abcdefgh'); -- 長(zhǎng)度8 > 5 -- 警告:Data truncated for column 'name' -- 結(jié)果:name字段值為'abcde'(截?cái)嗪螅?,?shù)據(jù)寫(xiě)入成功
嚴(yán)格模式(啟用STRICT_TRANS_TABLES):
INSERT INTO test_strict VALUES (1, 'abcdefgh'); -- 報(bào)錯(cuò):Data truncation: Data too long for column 'name' -- 結(jié)果:數(shù)據(jù)寫(xiě)入失敗,保持?jǐn)?shù)據(jù)完整性
4.2 數(shù)據(jù)有效性校驗(yàn)相關(guān)
| 模式值 | 作用說(shuō)明 |
|---|---|
| NO_ZERO_IN_DATE | 禁止日期中的"月/日"為0(如’2025-00-10’、‘2025-01-00’),嚴(yán)格模式下報(bào)錯(cuò) |
| NO_ZERO_DATE | 禁止插入"全零日期"(‘0000-00-00’),嚴(yán)格模式下報(bào)錯(cuò) |
| ERROR_FOR_DIVISION_BY_ZERO | 禁止"除以零"操作:整數(shù)除法報(bào)錯(cuò),浮點(diǎn)數(shù)除法返回NULL并警告 |
| NO_AUTO_VALUE_ON_ZERO | 插入自增字段時(shí),禁止0作為自增值 |
示例:禁止全零日期
-- 啟用NO_ZERO_DATE + 嚴(yán)格模式
SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_DATE';
INSERT INTO test_date (create_time) VALUES ('0000-00-00');
-- 報(bào)錯(cuò):Invalid datetime value: '0000-00-00'4.3 語(yǔ)法兼容與規(guī)范相關(guān)
| 模式值 | 作用說(shuō)明 |
|---|---|
| ONLY_FULL_GROUP_BY | 分組查詢嚴(yán)格限制:SELECT后的字段必須是GROUP BY中的字段,或被聚合函數(shù)包裹 |
| ANSI_QUOTES | 啟用后,字符串只能用單引號(hào),雙引號(hào)視為標(biāo)識(shí)符(兼容SQL標(biāo)準(zhǔn)) |
| PIPES_AS_CONCAT | 把` |
| IGNORE_SPACE | 允許函數(shù)名和括號(hào)之間有空格,如SUM (1+2) |
| NO_ENGINE_SUBSTITUTION | 指定的存儲(chǔ)引擎不存在時(shí)直接報(bào)錯(cuò),而非自動(dòng)替換 |
示例1:ONLY_FULL_GROUP_BY
-- 禁用ONLY_FULL_GROUP_BY(寬松) SET SESSION sql_mode = ''; SELECT name, age FROM user GROUP BY name; -- 結(jié)果:返回每個(gè)name對(duì)應(yīng)的第一條age(結(jié)果不確定?。? -- 啟用ONLY_FULL_GROUP_BY(嚴(yán)格) SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY'; SELECT name, age FROM user GROUP BY name; -- 報(bào)錯(cuò):age未分組且未聚合 -- 正確寫(xiě)法 SELECT name, AVG(age) FROM user GROUP BY name; -- 或使用ANY_VALUE()取任意值 SELECT name, ANY_VALUE(age) FROM user GROUP BY name;
示例2:PIPES_AS_CONCAT(兼容Oracle)
-- 啟用PIPES_AS_CONCAT SET SESSION sql_mode = 'PIPES_AS_CONCAT'; SELECT 'Hello' || ' ' || 'MySQL' AS result; -- 結(jié)果:'Hello MySQL'(等同于CONCAT函數(shù))
4.4 預(yù)定義模式組合
MySQL提供了幾個(gè)預(yù)定義的模式組合,本質(zhì)是多值的快捷方式:
| 組合模式 | 包含的模式值 | 適用場(chǎng)景 |
|---|---|---|
| ANSI | REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE | 兼容SQL標(biāo)準(zhǔn),適合多數(shù)據(jù)庫(kù)遷移 |
| TRADITIONAL | STRICT_TRANS_TABLES, STRICT_ALL_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_ENGINE_SUBSTITUTION | 傳統(tǒng)嚴(yán)格模式,模擬嚴(yán)格的數(shù)據(jù)庫(kù)行為 |
| ALLOW_INVALID_DATES | 僅校驗(yàn)日期格式,不校驗(yàn)日期有效性 | 兼容舊系統(tǒng)的非法日期數(shù)據(jù) |
-- 一鍵啟用ANSI兼容模式 SET GLOBAL sql_mode = 'ANSI'; -- 一鍵啟用傳統(tǒng)嚴(yán)格模式 SET GLOBAL sql_mode = 'TRADITIONAL'; -- 基于組合模式自定義 SET GLOBAL sql_mode = 'TRADITIONAL,STRICT_TRANS_TABLES'; -- 去掉STRICT_ALL_TABLES
5. 深入理解:為什么默認(rèn)沒(méi)有STRICT_ALL_TABLES?
5.1 核心原因
- 主流場(chǎng)景已被覆蓋:現(xiàn)在絕大多數(shù)業(yè)務(wù)使用InnoDB事務(wù)表,
STRICT_TRANS_TABLES已能滿足嚴(yán)格校驗(yàn)需求 - 避免非事務(wù)表的數(shù)據(jù)碎片:對(duì)MyISAM表啟用嚴(yán)格模式,可能導(dǎo)致批量插入中斷,造成部分?jǐn)?shù)據(jù)寫(xiě)入成功、部分失敗
- 歷史兼容性:避免舊系統(tǒng)升級(jí)后大面積報(bào)錯(cuò)
5.2 模式組合的價(jià)值
| 組合 | 一句話總結(jié) | 使用場(chǎng)景 |
|---|---|---|
| TRADITIONAL | “我要最嚴(yán)格的校驗(yàn)” | 金融、電商等核心業(yè)務(wù) |
| ANSI | “我要兼容其他數(shù)據(jù)庫(kù)” | 從Oracle/SQL Server遷移 |
| ALLOW_INVALID_DATES | “我要容忍舊數(shù)據(jù)的非法日期” | 遺留系統(tǒng)數(shù)據(jù)遷移 |
6. 常見(jiàn)問(wèn)題與解決方案
6.1 分組查詢報(bào)錯(cuò):ONLY_FULL_GROUP_BY
錯(cuò)誤信息:
Expression #2 of SELECT list is not in GROUP BY clause... this is incompatible with sql_mode=only_full_group_by
解決方案:
-- 方案1:優(yōu)化SQL(推薦) SELECT name, ANY_VALUE(age), AVG(score) FROM student GROUP BY name; -- 方案2:臨時(shí)關(guān)閉嚴(yán)格模式(不推薦) SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', '')); -- 方案3:永久關(guān)閉(不推薦生產(chǎn)) -- 在my.cnf中移除ONLY_FULL_GROUP_BY
6.2 插入零日期報(bào)錯(cuò)
錯(cuò)誤信息:
Invalid datetime value: '0000-00-00'
解決方案:
-- 方案1:修正數(shù)據(jù)(推薦) UPDATE table SET date_col = '1970-01-01' WHERE date_col = '0000-00-00'; -- 方案2:臨時(shí)允許零日期 SET SESSION sql_mode = (SELECT REPLACE(REPLACE(@@sql_mode, 'NO_ZERO_DATE', ''), 'NO_ZERO_IN_DATE', '')); -- 方案3:修改表結(jié)構(gòu),允許NULL ALTER TABLE table MODIFY date_col DATE NULL;
6.3 除法運(yùn)算報(bào)錯(cuò)
錯(cuò)誤信息:
Division by 0
解決方案:
-- 方案1:使用NULLIF避免除零 SELECT 100 / NULLIF(0, 0); -- 返回NULL -- 方案2:使用CASE語(yǔ)句 SELECT CASE WHEN divisor = 0 THEN NULL ELSE dividend / divisor END; -- 方案3:臨時(shí)關(guān)閉除零檢查 SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'ERROR_FOR_DIVISION_BY_ZERO', ''));
6.4 Oracle遷移時(shí)||連接符無(wú)效
-- 問(wèn)題:Oracle的||在MySQL中不生效 SELECT 'Hello' || 'World'; -- MySQL中返回0(默認(rèn)行為) -- 解決方案:?jiǎn)⒂肞IPES_AS_CONCAT SET GLOBAL sql_mode = CONCAT(@@GLOBAL.sql_mode, ',PIPES_AS_CONCAT'); FLUSH PRIVILEGES;
7. 生產(chǎn)環(huán)境最佳實(shí)踐
7.1 推薦配置
[mysqld] # MySQL 5.7/8.0通用推薦配置 sql_mode = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"
7.2 環(huán)境一致性原則
避免:開(kāi)發(fā)環(huán)境寬松、生產(chǎn)環(huán)境嚴(yán)格導(dǎo)致的"本地正常,線上報(bào)錯(cuò)"。
7.3 遷移場(chǎng)景的臨時(shí)調(diào)整
-- 步驟1:臨時(shí)放寬限制導(dǎo)入數(shù)據(jù) SET GLOBAL sql_mode = 'ALLOW_INVALID_DATES'; -- 步驟2:導(dǎo)入舊數(shù)據(jù) SOURCE old_data.sql; -- 步驟3:修正非法數(shù)據(jù) UPDATE table SET date_col = '1970-01-01' WHERE date_col = '0000-00-00'; -- 步驟4:恢復(fù)嚴(yán)格模式 SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
8. 各模式速查表
| 模式 | 分類 | 作用 | 推薦 |
|---|---|---|---|
| STRICT_TRANS_TABLES | 嚴(yán)格模式 | 事務(wù)表嚴(yán)格校驗(yàn) | ????? |
| ONLY_FULL_GROUP_BY | 語(yǔ)法規(guī)范 | 分組查詢嚴(yán)格限制 | ????? |
| NO_ZERO_DATE | 數(shù)據(jù)校驗(yàn) | 禁止零日期 | ????? |
| ERROR_FOR_DIVISION_BY_ZERO | 數(shù)據(jù)校驗(yàn) | 除零報(bào)錯(cuò) | ???? |
| NO_ENGINE_SUBSTITUTION | 語(yǔ)法規(guī)范 | 引擎不存在時(shí)報(bào)錯(cuò) | ???? |
| PIPES_AS_CONCAT | 語(yǔ)法兼容 | ` | |
| ANSI_QUOTES | 語(yǔ)法兼容 | 雙引號(hào)作為標(biāo)識(shí)符 | ??(遷移場(chǎng)景) |
| STRICT_ALL_TABLES | 嚴(yán)格模式 | 所有表嚴(yán)格校驗(yàn) | ?(非必要不開(kāi)啟) |
總結(jié)
sql_mode是MySQL數(shù)據(jù)質(zhì)量和語(yǔ)法兼容的核心配置,理解它對(duì)于保障數(shù)據(jù)一致性、避免生產(chǎn)事故至關(guān)重要:
- 生產(chǎn)環(huán)境建議啟用嚴(yán)格模式,阻止無(wú)效數(shù)據(jù)寫(xiě)入
- 開(kāi)發(fā)測(cè)試環(huán)境應(yīng)與生產(chǎn)保持一致,避免環(huán)境差異導(dǎo)致的問(wèn)題
- 遷移場(chǎng)景可臨時(shí)放寬限制,但完成后務(wù)必恢復(fù)嚴(yán)格模式
- 理解每個(gè)模式的作用,根據(jù)業(yè)務(wù)場(chǎng)景靈活配置
記住一句話:嚴(yán)格模式可能會(huì)讓你在開(kāi)發(fā)時(shí)多花10分鐘調(diào)試,但寬松模式可能會(huì)讓你在運(yùn)維時(shí)花10小時(shí)處理臟數(shù)據(jù)。
到此這篇關(guān)于MySQL sql_mode從入門(mén)到精通的文章就介紹到這了,更多相關(guān)MySQL sql_mode 入門(mén)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 淺談mysql的sql_mode可能會(huì)限制你的查詢
- MySQL報(bào)錯(cuò)sql_mode=only_full_group_by的問(wèn)題解決
- mysql5.7版本因?yàn)閟ql_mode設(shè)置導(dǎo)致的問(wèn)題以及解決
- mysql 8.0 找不到my.ini配置文件以及報(bào)sql_mode=only_full_group_by解決方案
- MySQL中sql_mode模式的使用
- MySQL配置sql_mode的參數(shù)屬性作用
- mysql怎么關(guān)閉sql_mode=ONLY_FULL_GROUP_BY模式
- mysql?sql_mode數(shù)據(jù)驗(yàn)證檢查方法
相關(guān)文章
mysql oracle和sqlserver分頁(yè)查詢實(shí)例解析
最近簡(jiǎn)單的對(duì)oracle,mysql,sqlserver2005的數(shù)據(jù)分頁(yè)查詢作了研究,把各自的查詢的語(yǔ)句貼到腳本之家平臺(tái)供大家參考2017-10-10
mysql lpad函數(shù)和rpad函數(shù)的使用詳解
MySQL中的LPAD和RPAD函數(shù)用于字符串填充,LPAD從左至右填充,RPAD從右至左填充,兩者都可指定填充長(zhǎng)度和填充字符,如果填充長(zhǎng)度小于原字符串長(zhǎng)度,則會(huì)截取原字符串相應(yīng)長(zhǎng)度的字符2025-02-02
MySQL 事務(wù)與鎖機(jī)制詳解及注意事項(xiàng)
MySQL 的事務(wù)與鎖機(jī)制共同構(gòu)成了數(shù)據(jù)庫(kù)并發(fā)控制的核心,通過(guò)遵循 ACID 原則和合理設(shè)置事務(wù)隔離級(jí)別,可以有效地保障數(shù)據(jù)的一致性和完整性,這篇文章主要介紹了MySQL 事務(wù)與鎖機(jī)制詳解,需要的朋友可以參考下2025-04-04
MySQL?DDL執(zhí)行方式Online?DDL詳解
這篇文章主要介紹了MySQL?DDL執(zhí)行方式Online?DDL詳解,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下2022-09-09
SELECT… FOR UPDATE 排他鎖的實(shí)現(xiàn)
本文主要介紹了SELECT… FOR UPDATE 排他鎖的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01

