gorm动态条件筛选核心是“只追加不重置”,where链式调用累积子句且不自动忽略空值,需显式判断或用map/结构体过滤零值,复杂逻辑须用嵌套where、子查询或scope实现。

动态条件筛选不是拼SQL,而是增量构造查询对象;核心是“只追加不重置”,且必须显式跳过空值或无效条件。
Where 链式调用本质是累积,不是模板渲染
GORM 的 Where 每次调用都往当前查询对象里追加一个 WHERE 子句,不会清空之前的内容,也不会自动忽略 nil 或空字符串。比如:
db.Where("name = ?", name).Where("status = ?", status).Find(&users)
若 name == "",它仍会生成 WHERE name = '' AND status = 1,结果可能为空——这不是你想要的“忽略 name 条件”。
- 正确做法是显式判断:
if name != "" { db = db.Where("name LIKE ?", "%"+name+"%") } - 或者用
map[string]interface{}自动过滤零值(但注意:它不支持LIKE、!=、BETWEEN等操作符) - 结构体传参(如
db.Where(&User{Name: name}))也会跳过零值字段,但无法表达status = 0这类合法零值条件
Or/Not 容易因优先级出错,必须用嵌套 Where 分组
直接链式写 db.Where("a = ?", 1).Or("b = ?", 2).Where("c = ?", 3),生成的是 WHERE a = 1 OR b = 2 AND c = 3,因为 SQL 中 AND 优先级高于 OR。
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
- 要实现
(a = 1 OR b = 2) AND c = 3,必须把 OR 包进子查询:db.Where(db.Where("a = ?", 1).Or("b = ?", 2)).Where("c = ?", 3) -
Not同理,db.Not("status = ?", 0)是单条件否定;想否定多个字段组合,得用db.Not(map[string]interface{}{"status": 0, "type": "tmp"}),它会生成NOT (status = 0 AND type = 'tmp') - 复杂逻辑建议拆成变量,比如先定义
base := db.Where("tenant_id = ?", tid),再按需扩展
依赖另一结果集的条件,必须用子查询而非 Join
当筛选逻辑依赖聚合或独立数据集时,例如“查最近 7 天有 ≥2 笔已支付订单的用户”,仅靠 Joins + Where 无法表达分组后过滤。
- 安全写法只有两种:
db.Where("id IN ?", db.Table("orders").Select("user_id").Where("status = ? AND created_at > ?", "paid", sevenDaysAgo).Group("user_id").Having("COUNT(*) >= 2")) - 或者封装进
Scope,避免重复逻辑 - 绝对禁用字符串拼接子查询(如
"id IN (SELECT ...)"),否则绕过参数绑定,引发 SQL 注入 - 性能上,
IN子查询在结果集大时可能触发全表扫描;PostgreSQL 尤其敏感,务必用EXPLAIN验证执行计划
接口层接收 Query 参数后,别直接塞进 Where
Gin 的 c.Query("name") 只返回 string,不做类型或合法性校验。直接 strconv.Atoi(c.Query("page")) 可能 panic。
- 推荐用结构体绑定:
c.ShouldBindQuery(&opts),配合bindingtag 做min=1、oneof=active,inactive等校验 - 字段必须首字母大写(exported),否则绑定静默失败
- 对需要模糊匹配的字段(如 keyword),绑定后手动加通配符:
if opts.Keyword != "" { db = db.Where("name LIKE ?", "%"+opts.Keyword+"%") } - 分页慎用
Offset,大数据量下性能崩盘;游标分页更稳,比如用WHERE id > ? ORDER BY id LIMIT 20
最常被忽略的一点:子查询和主查询的表名、字段名、参数绑定必须完全隔离;一旦混用 db.Table() 和链式 Where,尤其在分表场景下,很容易查到默认表而不是目标分表。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










