order() 必须在 limit/offset 前以确保排序生效,否则跨页数据跳变或重复;字段需带确定性(如 id desc);group() 后用 having() 过滤;子查询须用 (?) 包裹;struct 接收时别名须与字段名严格一致。

Order() 必须出现在 Limit/Offset 之前,否则排序失效
GORM 的链式调用是“后置生效”,但 Order() 的位置直接影响结果一致性。如果写成 db.Where().Limit(10).Offset(20).Order("created_at DESC"),数据库实际执行的是先取 20 行之后的 10 行,再对这 10 行排序——跨页时记录会跳变或重复。
常见错误现象:翻页时某条数据突然消失;第 2 页第一条和第 1 页最后一条内容相同;总数和分页列表对不上。
- ✅ 正确顺序:
db.Where().Order("id DESC").Limit(10).Offset(20) - 字段必须带确定性:单用
Order("status ASC")不可靠,status 值重复太多,数据库无法稳定排序 - 高并发下
created_at可能毫秒级重复,建议补上, id DESC作为第二排序键 - 别用
Order("RAND()")分页——不支持续查,且全表扫描性能极差
Group() 后必须用 Having() 过滤聚合结果,Where() 无效
Where() 作用于原始行,Having() 才作用于 GROUP BY 后的分组聚合值。比如“查日订单总额超 1000 的日期”,若用 Where 写 Where("SUM(amount) > 1000"),GORM 会报错或生成非法 SQL,因为 SUM 是聚合函数,不能出现在 WHERE 子句中。
典型错误直觉:“我 Group 了,再加个 Where 就行”——这是 SQL 基础语义混淆。
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
- ✅ 正确写法:
db.Table("orders").Select("DATE(created_at) as date, SUM(amount) as total").Group("DATE(created_at)").Having("SUM(amount) > ?", 1000).Scan(&results) - 多个分组字段要一起写进 Group(),如
Group("user_id, status"),不能拆开 - Having 中的字段名必须是 SELECT 中出现的别名或原始列,不能是未选中的字段
- PostgreSQL 对 Having 更严格,MySQL 有时会隐式允许,但别依赖
子查询嵌套在 Where 中必须用 (?) 包裹,否则不是子查询
当需要“查销售额超过平均值的用户”这类逻辑时,不能靠 Join 或 Where 拼接解决。GORM 要求子查询对象必须显式传入 Where("total_sales > (?)", subQuery),括号 (?) 是关键语法标记,告诉 GORM 这里要内联一个子查询,而非普通参数。
常见错误现象:SQL 报错 “Operand should contain 1 column(s)”;生成的语句变成 WHERE total_sales > 'SELECT AVG(...)'(字符串字面量);或直接触发 SQL 注入。
- ✅ 安全写法:
avgSub := db.Table("sales").Select("AVG(amount)");db.Model(&User{}).Where("total_sales > (?)", avgSub).Find(&users) - NOT IN / IN 场景也一样:
Where("id NOT IN (?)", db.Table("logins").Select("user_id")) - 绝对禁止字符串拼接:
Where("id IN (SELECT user_id FROM logs WHERE ...)" + strconv.Itoa(id))—— 等于放弃参数绑定 - 子查询结果集大时,MySQL 可能自动转 JOIN,但 PostgreSQL 不会,务必用
EXPLAIN验证执行计划
Struct 接收分组结果时字段名必须与 Select 别名完全一致
GORM Scan 到 struct 时不做字段映射或大小写折叠,Select("SUM(amount) as total") 中的 as total 必须对应 struct 字段名 Total int(首字母大写),且 tag 不影响列名匹配。
容易忽略的点:Go struct 字段默认导出(首字母大写),但别名写成小写 as total,而 struct 写成 total int(未导出),Scan 会静默失败,results 为空 slice 且无 error。
- ✅ 正确定义:
type Result struct { Date string `gorm:"column:date"`; Total int `gorm:"column:total"` } - 别名和 struct 字段名区分大小写,
as TOTAL就得配TOTAL int - 用
db.Debug()开启日志,确认生成的 SQL 中 AS 别名拼写是否与 struct 字段一致 - 临时调试可用 map[string]interface{} 接收,避免 struct 字段名歧义










