反引号能解决问题是因为它强制mysql将order、desc等识别为标识符而非关键字,避免语法错误;必须用于字段名或表名是mysql保留字的场景,如select order、insert into new (image)等,但长期应重命名避开关键字。

直接用 ` 包住字段名就能解决,但必须是英文键盘上 ESC 下方、TAB 上方那个键打出的反引号,不是单引号 ',也不是双引号 "。
为什么反引号能解决问题
MySQL 解析器看到 `order` 或 `desc` 时,会明确将其识别为「标识符」(identifier),即表名、列名这类用户自定义对象;而裸写 order 或 desc,解析器优先按关键字(如 ORDER BY、DESC 排序)处理,导致语法错误。
这种机制是 SQL 标准的一部分,MySQL 用反引号实现,PostgreSQL/SQL Server 用双引号,SQLite 也支持反引号 —— 但跨数据库迁移时仍需注意兼容性。
哪些地方必须加反引号
只要字段名(或表名)本身是 MySQL 关键字,以下所有场景都必须显式包裹:
-
SELECT查询中涉及该字段:例如SELECT `read`, `key`, `order` FROM users -
INSERT语句的列名列表:例如INSERT INTO logs (`level`, `time`) VALUES (?, ?) -
UPDATE的SET和WHERE子句:例如UPDATE config SET `value` = 'on' WHERE `key` = 'feature_flag' - 在
JOIN或子查询中引用时:例如SELECT u.`name`, o.`order` FROM users u JOIN orders o ON u.id = o.user_id
漏掉任意一处,都会触发 ERROR 1064 (42000) 类语法错误,且报错位置往往不直观(比如提示“near 'order FROM...'”而不是“字段 order 未加引号”)。
MyBatis / MyBatis-Plus 中怎么写
框架不会自动帮你加反引号,得手动处理:
- XML 映射文件里,直接在字段名上加:
<insert>INSERT INTO user_log (`action`, `time`) VALUES (#{action}, #{time})</insert> - MyBatis-Plus 实体类中,用
@TableField指定带反引号的列名:@TableField("`desc`") private String description; - 如果用 LambdaQueryWrapper,别写
User::getDesc这种方法引用 —— 它生成的 SQL 不会自动加反引号,必须改用字符串形式:wrapper.eq("desc", "active"),或确保字段名已重命名避开关键字
特别注意:MyBatis-Plus 的 @TableName 和 @TableId 同样需要反引号包裹关键字表名或主键名,例如 @TableName("`order`")。
反引号不是万能解药
它只是“绕过解析错误”的临时手段,不是设计规范:
- 所有 SQL 工具(Navicat、DBeaver)、ORM、甚至某些连接池(如 HikariCP 开启
cachePrepStmts=true)对含反引号的语句缓存行为可能不一致 - 团队协作中,别人查表结构时看不到反引号,容易误以为字段名就是
order,结果写 SQL 时不加又报错 - 导出 DDL(
SHOW CREATE TABLE)会原样保留反引号,但下游系统(如 Flink CDC、DataX)可能不识别,导致同步失败
真正要长期维护的项目,建议优先用 ALTER TABLE ... CHANGE 重命名字段,比如把 `order` 改成 order_no —— 反引号只留给历史包袱太重、无法修改 schema 的紧急场景。











