当前位置:首页 > 问答 > 正文

MySQL里那些转义字符到底怎么用,实际操作中常见的坑和解决办法

说到MySQL里的转义字符,最核心、最容易让人迷糊的就是反斜杠(\),很多人觉得它很简单,不就是用来转义特殊字符的嘛,但实际操作中,你会在两个不同的地方遇到它,而这两个地方的规则还不太一样,这就是大多数坑的来源,这两个地方分别是:SQL语句本身MySQL的LIKE查询

第一个层面:SQL语句字符串中的转义

当你写一条SQL语句,比如一个INSERT语句,值是用单引号包起来的字符串,如果这个字符串里本身就包含单引号,怎么办?直接写会破坏SQL语法。

-- 错误的写法:字符串中的单引号与包围它的单引号冲突了
INSERT INTO users (name) VALUES ('O'Reilly');

这时候就需要转义,在标准的SQL字符串中,反斜杠(\)是默认的转义字符,所以正确的写法是:

-- 正确的写法:用反斜杠转义内部单引号
INSERT INTO users (name) VALUES ('O\'Reilly');

这样,MySQL在解析SQL语句时,会知道这个\'是一个普通的单引号字符,而不是字符串的结束标记,常见的需要这样转义的字符还包括双引号(\")、反斜杠本身(\\)等。

这里就来了第一个常见的坑:NO_BACKSLASH_ESCAPES模式。

MySQL里那些转义字符到底怎么用,实际操作中常见的坑和解决办法

MySQL提供了一个SQL模式(sql_mode)叫做NO_BACKSLASH_ESCAPES,如果这个模式被开启了,那么反斜杠(\)就失去了它转义字符的超能力,它会变成一个普通的字符。

在开启此模式后,你执行上面的语句INSERT INTO users (name) VALUES ('O\'Reilly');,MySQL不会把\'解释为单引号,而是直接理解为字面的“反斜杠”和“单引号”两个字符,这会导致插入数据库的名字变成O\'Reilly,并且如果字符串长度计算不准,还可能出错,更危险的是,如果这个字符串来自用户输入,而你没做处理,就可能引发SQL注入攻击,因为单引号没有被正确转义。

怎么办?

  1. 知道它的存在:首先要意识到你的MySQL服务器可能启用了这个模式,可以通过执行SELECT @@sql_mode;来查看。
  2. 统一转义方式:如果启用了NO_BACKSLASH_ESCAPES,标准SQL提供了另一种转义单引号的方法:用两个单引号
    -- 在NO_BACKSLASH_ESCAPES模式下,这样写是安全的
    INSERT INTO users (name) VALUES ('O''Reilly');

    这种方式在任何SQL模式下都有效,可移植性更好,很多编程语言的数据访问框架(如Python的SQLAlchemy、Java的MyBatis)默认都会采用这种方式来处理字符串中的引号。

    MySQL里那些转义字符到底怎么用,实际操作中常见的坑和解决办法

第二个层面,也是更大的一个坑:LIKE查询中的通配符转义。

在LIKE查询中,百分号(%)和下划线(_)是通配符。%代表任意多个字符,代表一个字符,如果你想搜索的内容里本身就包含%或,比如想找名字叫“张_三”的员工,直接写:

-- 错误的写法:这里的下划线会被当作通配符,会匹配到“张三”、“张老三”、“张阿三”等等
SELECT * FROM employees WHERE name LIKE '张_三';

这显然不是你想要的,这时候,你同样需要转义,问题是,用什么转义?还是反斜杠吗?大多数情况下,是的,MySQL默认的转义字符就是反斜杠。

-- 正确的写法:用反斜杠转义下划线
SELECT * FROM employees WHERE name LIKE '张\_三';

这样,MySQL就知道这个_是字面意义上的下划线,而不是通配符。

MySQL里那些转义字符到底怎么用,实际操作中常见的坑和解决办法

但这里藏着第二个大坑:如果同时开启了NO_BACKSLASH_ESCAPES模式怎么办?

NO_BACKSLASH_ESCAPES模式下,反斜杠不生效了,那你用LIKE '张\_三'去查,\_不会被转义,查询依然会出错。

解决办法:使用ESCAPE子句自定义转义符。

这是最可靠、最推荐的做法,LIKE语句允许你使用ESCAPE关键字来指定一个临时的、仅对本次查询有效的转义字符,这样你就可以完全摆脱对默认反斜杠的依赖,无论SQL模式如何设置,代码行为都是一致的。

-- 使用ESCAPE指定用哪个字符作为转义符,这里我用一个不常用的字符,#39;/''
SELECT * FROM employees WHERE name LIKE '张/_三' ESCAPE '/';
-- 意思就是:告诉MySQL,在这个LIKE模式里,紧跟在'/'后面的字符,请把它当作普通字符处理。

甚至你可以用、等任何你喜欢的字符作为转义符,这种方法清晰、明确,避免了环境配置带来的意外。

总结一下实际操作中的建议:

  1. 处理普通字符串:如果可能,优先考虑使用两个单引号()来转义单引号,这比依赖反斜杠更安全、更通用。
  2. 处理LIKE查询养成习惯,总是使用ESCAPE子句来显式指定转义字符,特别是当你的程序可能运行在不同配置的MySQL服务器上时,这是避免坑的最有效方法。
  3. 了解你的环境:在写代码前,或者程序出现诡异问题时,查一下数据库的sql_mode设置,看看是否包含了NO_BACKSLASH_ESCAPES等可能影响转义行为的模式。
  4. 永远不要相信用户输入:在将用户输入拼接到SQL语句(尤其是LIKE语句)中之前,必须在应用层代码中,对可能存在的通配符(%, _)和转义符(\)进行适当的处理或转义,不能仅仅依赖数据库层面的转义,因为这涉及到SQL注入的安全问题,最好的方式是使用参数化查询(Prepared Statements),从根本上杜绝这类问题。

归根结底,MySQL转义字符的“坑”主要源于规则在不同上下文(普通字符串 vs LIKE模式)和不同配置(sql_mode)下的不一致性,理解了这一点,并有意识地采用更健壮的方法(如ESCAPE子句和参数化查询),就能有效地避开它们。