很多同学写 SQL 的时候,都熟悉#、--、/* */这几种普通注释。普通注释的特点很简单:数据库解析时直接忽略里面的内容,不会执行。
但 MySQL 里有一类特殊注释,在 MySQL 环境下注释内部的 SQL 会被执行;其他数据库(Oracle、PostgreSQL)会当成普通注释直接跳过,这就是MySQL 条件注释,也常被叫做版本注释、可执行注释。
一句话总结:条件注释是 MySQL 提供的,用来做跨数据库、跨版本 SQL 兼容的语法。
一、基础语法
/*! 这里写MySQL才会执行的SQL语句 */格式:/*!开头,*/结尾。
- MySQL:会把
/*!和*/中间的内容当做正常SQL执行 - 其他数据库:识别为普通多行注释,直接忽略,不会报错
带版本号的条件注释(最常用)
还可以在!后面加上版本号,代表只有MySQL版本大于等于指定版本时,才执行内部SQL
/*!40101 SET NAMES utf8mb4 */;解读:
40101= 4.1.01 版本- MySQL >=4.1.01 时,执行
SET NAMES utf8mb4 - 版本低于该值 或者 非MySQL数据库,当作注释忽略
版本号规则:主版本*10000 + 次版本*100 + 修订号
示例:
- 8.0.26 → 80026
- 5.7.36 → 50736
二、简单示例演示
示例1:基础条件注释
-- 普通SQLSELECT1;/*! SELECT 2; */在MySQL执行,会返回两个结果集:1和2;
如果放到PostgreSQL执行,只会执行SELECT 1,SELECT 2被当成注释跳过。
示例2:版本控制,仅高版本执行
/*!80000 CREATE TABLE IF NOT EXISTS test_v8(id int) */;含义:MySQL版本 ≥8.0.00 才会创建这张表,5.7及更早版本不执行。
我们经常导出mysqldump备份文件时,里面大量出现这种语法,这也是条件注释最典型的使用场景。打开dump文件就能看到大量
/*!40xxx ... */。
三、典型使用场景
- mysqldump导出备份
mysqldump 默认大量使用条件注释。导出的SQL脚本可以在不同版本MySQL导入,同时拿到其他数据库也不会因为MySQL特有语法报错。比如设置字符集、关闭外键检查等语句。
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;- 跨数据库兼容脚本
一份SQL脚本,同时兼容MySQL和其他数据库。特有语法写在条件注释里。
例如:只有MySQL支持的LIMIT,其他数据库不支持,就可以这样写:
SELECT*FROMuser/*! LIMIT 10 */;在PostgreSQL执行不会识别LIMIT,不会执行这部分;MySQL会正常执行limit。
- 版本平滑适配
新特性只在高版本MySQL启用,低版本跳过。比如MySQL8.0支持的窗口函数、新的排序规则,用版本条件注释包裹,一份脚本兼容5.7和8.0。
四、容易踩的坑
- 注释内部语法错误依然会报错
条件注释不是“屏蔽错误”,MySQL满足版本条件后,会直接解析里面的SQL。如果里面SQL写错,一样抛出异常。
/*!80000 SELEC 1 */;-- 拼写错误,8.0环境直接报语法错误不要和普通多行注释混淆
/* 内容 */:所有数据库都是注释,永远不执行。/*! 内容 */:MySQL会执行,其他库忽略。差别就在于那个感叹号。版本号写错,逻辑完全反转
版本号是大于等于才执行。如果你写/*!50700 ... */,在MySQL8.0环境依旧会执行,80000>50700。不适合业务代码大量滥用
条件注释适合迁移脚本、备份脚本。业务系统里SQL尽量不要大量依赖这个语法,可读性差,维护成本高。ORM框架一般不会推荐大量使用。
五、补充:反向理解
很多人第一次看到mysqldump里面的/*!40000 DROP TABLE IF EXISTS ... */很疑惑,以为是注释。
其实这是MySQL专门设计的兼容语法,为了让一份SQL脚本具备多环境兼容性,是MySQL生态里很经典的设计。
小结
MySQL条件注释/*! [版本] sql */,核心能力一句话:
一份SQL,在MySQL中可选择性执行内部语句,其它数据库自动忽略这段代码,用于版本适配、跨库脚本兼容。
它不是普通注释,是MySQL独有的编译期条件执行语法,最常见于mysqldump导出文件。日常业务SQL尽量少用,但是做数据库迁移、脚本分发的时候,这个特性就非常好用。