Yii中like查询需谨慎:默认前后加%但受排序规则影响;前缀匹配用false参数;多字段用or嵌套;避免函数导致索引失效;注意SQL注入风险,须转义通配符并绑定参数。

like 查询在 Yii 中不是“加个百分号就完事”的操作,它直接决定搜索性能、结果准确性,以及是否会被 SQL 注入绕过。用错参数或忽略上下文,轻则查不到数据,重则拖垮数据库。
为什么 andFilterWhere(['like', 'title', $keyword]) 有时查不到内容
默认行为会在 $keyword 前后自动加上 %,生成 WHERE title LIKE '%关键词%';但若字段值带空格、全角字符、或数据库排序规则不匹配(比如 utf8mb4_general_ci 对大小写/全半角不敏感),就可能漏匹配。
- 确保数据库字段使用
utf8mb4_unicode_ci或更严格的排序规则(如utf8mb4_0900_as_cs) - 如果只想要前缀匹配(如“苹果”匹配“苹果手机”但不匹配“iPhone 苹果”),显式传
false:andFilterWhere(['like', 'title', $keyword, false])→LIKE '关键词%' - 若需精确控制通配符位置,直接用
Expression:andFilterWhere(['like', 'title', new yiidbExpression("CONCAT('%', :kw, '%')", [':kw' => $keyword])])
like 和 ilike 在不同数据库的行为差异
MySQL 没有 ilike,靠排序规则实现大小写不敏感;PostgreSQL 原生支持 ILIKE,且对 Unicode 更友好。Yii 的 like 底层会根据当前 DBMS 自动适配,但要注意:
- 在 PostgreSQL 上,
['like', 'name', 'zhang']默认就是大小写不敏感的,无需额外处理 - 在 MySQL 上,即使用了
utf8mb4_unicode_ci,全角/半角“ABC” vs “ABC”仍可能不等价,此时应统一前端输入标准化(如转半角) - 避免在
like条件里套函数,如LOWER(title)—— 这会让索引完全失效
多字段同时模糊匹配的正确写法
用户搜“张三”,希望匹配 name 或 nickname,不能写两个 andFilterWhere(['like', ...]) 并用 andWhere 连接——那会变成 AND 逻辑,要求两字段都含关键词。
- 必须用操作符数组嵌套
or:andFilterWhere(['or', ['like', 'name', $keyword], ['like', 'nickname', $keyword]]) - 如果字段可能为 NULL,加一层
IS NOT NULL防止空值干扰:andFilterWhere(['or', ['and', ['is not', 'name', null], ['like', 'name', $keyword]], ['and', ['is not', 'nickname', null], ['like', 'nickname', $keyword]]]) - 性能敏感场景下,单字段模糊已超 5 万行,就别拼 OR 了——该上 Elasticsearch
容易被忽略的注入风险点
andFilterWhere 能防基础注入,但一旦混入手动拼接或 Expression,防线就断了。
- 绝对不要这样写:
andFilterWhere(['like', 'title', '%' . $_GET['q'] . '%'])——$_GET未过滤,%和_是通配符,可被利用做盲注 - 用
Expression时必须绑定参数:new yiidbExpression("title LIKE :pattern", [':pattern' => "%{$keyword}%"]) - 前端传来的
$keyword若含%或_,需转义:str_replace(['%', '_'], ['%', '_'], $keyword),再在like中指定 escape:andFilterWhere(['like', 'title', $escapedKeyword, false, '\'])
like 看似简单,但每个参数位置、每个 escape 字符、每种数据库方言,都在悄悄影响结果。


















