gorm模糊查询需用where+like手动拼接%通配符,前缀匹配用"%张"、后缀用"三%"、全模糊用"%李%";须防空格和重复加%、禁用结构体传参、动态字段需白名单校验、注意索引与性能优化。

用 Where + LIKE 拼接通配符是最直接的方式
GORM 不提供独立的 Like 方法,而是通过 Where(或 Where 的变体)配合 SQL 的 LIKE 关键字实现模糊匹配。关键在于手动拼接 % 通配符,且必须注意位置和转义。
常见错误是把通配符写进数据库字段值里(比如存成 %张%),或者在 Go 字符串里漏掉 % 导致查不到结果。
- 前缀匹配(以“张”开头):
db.Where("name LIKE ?", "%张").Find(&users) - 后缀匹配(以“三”结尾):
db.Where("name LIKE ?", "三%").Find(&users) - 全模糊(包含“李”):
db.Where("name LIKE ?", "%李%").Find(&users) - 区分大小写?取决于数据库 collation,MySQL 默认不区分,PostgreSQL 默认区分 —— 需要用
ILIKE(PostgreSQL)或LOWER()包裹字段和参数
用 Scope 封装常用模糊查询逻辑更安全
每次手拼 % 容易出错,尤其当搜索词来自用户输入时,还可能被注入恶意字符(虽然 GORM 的问号占位符已防 SQL 注入,但语义错误仍会发生)。封装成 Scope 或普通函数,能统一处理空值、trim 和通配符逻辑。
示例封装函数:
func NameLike(name string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if name == "" {
return db
}
return db.Where("name LIKE ?", "%"+strings.TrimSpace(name)+"%")
}
}
// 使用
db.Scopes(NameLike(input)).Find(&users)
- 必须对
input做strings.TrimSpace,否则前后空格会导致"% 张 %"这种无效模式 - 不要在函数外提前加
%,否则调用方容易重复添加,变成"%%张%%" - 如果要支持多字段(如 name 和 email),建议拆成多个独立 scope,避免逻辑耦合
用 Find 的结构体参数传参会失效
有人尝试这样写:db.Where(&User{Name: "%张%"}).Find(&users),期望 GORM 自动识别 % 并生成 LIKE 查询 —— 这不会生效。GORM 对结构体参数只做等值匹配(=),不会解析通配符,最终生成的是 WHERE name = '%张%',查不到任何内容。
- 结构体传参仅适用于精确匹配场景
- 模糊搜索必须显式写 SQL 片段(如
"name LIKE ?") - 若字段名是动态的(比如根据前端传来的
field=name),需用fmt.Sprintf拼接字段名,但务必校验白名单,防止 SQL 注入(字段名无法用问号占位)
性能和索引注意事项
LIKE 查询是否走索引,高度依赖通配符位置和数据库配置。以 "%张" 开头的查询基本无法使用 B-tree 索引,而 "张%" 可以(前提是字段有索引)。
- 确保被模糊查询的字段建了索引:
db.Index("idx_users_name").Add("name") - 避免在大表上执行
"%关键词%",尤其是数据量 > 10 万行时,考虑引入全文检索(如 PostgreSQL 的tsvector或外部 Elasticsearch) - MySQL 8.0+ 支持函数索引,可建
INDEX ON users (LOWER(name))来优化大小写不敏感查询
真正难的不是写对那一行 Where,而是想清楚这个模糊查询在生产环境里跑得动吗、要不要加缓存、有没有替代方案 —— 这些往往比语法更重要。











