转义 MySQL 通配符

Posted

技术标签:

【中文标题】转义 MySQL 通配符【英文标题】:Escaping MySQL wild cards 【发布时间】:2010-09-10 10:04:36 【问题描述】:

在我正在使用的旧服务器上,我无法使用准备好的语句,我目前正试图在将用户输入发送到 mysql 之前完全转义它。 为此,我使用了 php 函数 mysql_real_escape_string

由于此函数不会 % 和 _,因此我也使用 addcslashes 转义这些。

当我发送类似:

test_test " ' 

到数据库再读回数据库显示:

test\_test " ' 

看着这个,我不明白为什么 _ 前面有反斜杠,但 " 和 ' 没有。 因为它们都用 \ 肯定 _ ' 进行了转义,并且 " 应该看起来都一样,即所有转义字符都可见或都不可见。

转义的 \s 是否会自动屏蔽掉

谁能解释一下?

【问题讨论】:

【参考方案1】:

_% 通常不是 MySQL 中的通配符,不应为了将它们放入普通字符串文字而对其进行转义。 mysql_real_escape_string 是正确且足够的。不应使用addcslashes

_% 仅在 LIKE-matching 的上下文中是特殊的。当您想在 LIKE 语句中准备字符串以供文字使用时,以便 100% 匹配 100% 而不仅仅是任何以 100 开头的字符串,您需要担心两个级别的转义。

第一个是 LIKE 转义。 LIKE 处理完全在 SQL 内部进行,如果您想将文字字符串转换为文字 LIKE 表达式,您必须执行此步骤即使您使用的是参数化查询

在这个方案中,_% 是特殊的,必须转义。转义字符也必须转义。根据 ANSI SQL,除了这些 不能 之外的字符被转义:\' 是错误的。 (虽然 MySQL 通常会让你侥幸逃脱。)

完成此操作后,您将进入第二级转义,即普通的旧字符串文字转义。这发生在 SQL 之外,创建 SQL,因此必须在 LIKE 转义步骤之后完成。对于 MySQL,这是 mysql_real_escape_string 和以前一样;对于其他数据库,会有不同的功能,您可以使用参数化查询来避免这样做。

这里导致混淆的问题是,在 MySQL 中,两个嵌套转义步骤都使用反斜杠作为转义字符!因此,如果您想将字符串与文字百分号匹配,则必须使用双反斜杠转义并说LIKE 'something\\%'。或者,如果这是在 PHP " 文字中,它也使用反斜杠转义,"LIKE 'something\\\\%'"。啊!

根据 ANSI SQL,这是不正确的,它说:在字符串文字中,反斜杠表示文字反斜杠,转义单引号的方法是 '';在 LIKE 表达式中,默认情况下根本没有转义字符。

因此,如果您想以可移植的方式 LIKE-escape,您应该使用 LIKE ... ESCAPE ... 构造覆盖默认(错误)行为并指定您自己的转义字符。为了理智,我们将选择该死的反斜杠以外的其他东西!

function like($s, $e) 
    return str_replace(array($e, '_', '%'), array($e.$e, $e.'_', $e.'%'), $s);


$escapedname= mysql_real_escape_string(like($name, '='));
$query= "... WHERE name LIKE '%$escapedname%' ESCAPE '=' AND ...";

或带参数(例如在 PDO 中):

$q= $db->prepare("... WHERE name LIKE ? ESCAPE '=' AND ...");
$q->bindValue(1, '%'.like($name, '=').'%', PDO::PARAM_STR);

(如果您想要更多的可移植性聚会时间,您还可以尝试考虑 MS SQL Server 和 Sybase,其中[ 字符在LIKE 语句中也错误地是特殊的并且必须被转义. 啊。)

【讨论】:

我会再次为“该死的反斜杠”+1。 谢谢,现在就吸收这个……这真的帮助我扩展了我的基础知识。愚蠢的是,即使我实际上并没有使用任何 LIKE 语句,我也在转义 % 和 _ 并且因为我认为(请确认)% 和 _ 仅在 LIKE 语句的上下文中是狂野的,我实际上是在浪费我的时间。但这让我想,当它在 LIKE 语句的上下文中时,你为什么要转义 % 或 _ 。当然,使用 LIKE 语句的唯一原因是您可以使用它的通配符。 (请原谅我对这方面的知识有限) 当然,但是希望能够搜索文字 %_ 字符是非常自然的。如果用户在前端搜索50%,他们可能意味着他们正在寻找包含50% 的字符串,而不仅仅是其中包含50 的任何字符串。 我无法编辑答案,但有一个小错误:在 str_replace() 行中:$e_ 是一个不存在的变量。相反,请使用“$e_” 在使用例如 utf8mb4 语言特定排序规则时还要注意bugs.mysql.com/bug.php?id=39808(请参阅此处的列表hastebin.com/acoqedajij)。【参考方案2】:

惊讶了这么多年没有人提起它,但如果你不需要做复杂的通配符匹配(例如foo%baz),我认为INSTR/LOCATE/POSITION,@987654325 @、RIGHT 等就足够了。在我的所有情况下,我只使用LIKE 匹配字符串中的任何位置(例如%foobar%),所以在所有关于转义LIKE 模式的恐怖故事之后,我现在使用INSTR而是。

等价于value LIKE '%foobar%'(匹配任何地方):

INSTR(value, 'foobar') > 0

等价于value LIKE 'foobar%'(开始时匹配):

INSTR(value, 'foobar') = 1

等价于value LIKE '%foobar'(最后匹配):

RIGHT(value, 6) = 'foobar'

它可能不那么直接和容易记住,最后匹配的解决方案可能会以某种方式进行改进以使其更加通用。但是这些替代方案至少应该让您在安全方面放心,因为它绕过了任何自滚转义的需要,并且不需要您更改实际参数值(无论如何使用准备好的语句时)。

【讨论】:

以上是关于转义 MySQL 通配符的主要内容,如果未能解决你的问题,请参考以下文章

mysql 条件查询逻辑查询模糊查询通配符转义字符

MySQL模糊查询特殊字符如何查询

MySQL 学习三 关于转义

mysql模糊查询固定某个字符

oracle 插入所有通配符 转义字符 特殊符号

Android SQLite LIKE 转义通配符