group + select + scan 必须配对使用,单独调用 group 不会自动返回聚合字段;必须显式用 select 声明聚合表达式(如 "date(created_at) as date, sum(amount) as total"),并确保别名与结构体字段名严格匹配(大小写敏感),否则 scan 会静默跳过赋值。

Group + Select + Scan 必须配对使用
单独调用 Group 不会自动返回聚合字段,GORM 不像 SQL 那样隐式推导 SELECT 列。如果你只写 db.Model(&Order{}).Group("date(created_at)") ,后续 Scan 会报错或返回空结构体——因为没指定要查什么。
正确做法是:必须显式用 Select 声明聚合表达式,并确保字段别名与接收结构体字段名严格匹配(大小写敏感)。
-
Select中的别名(如total)必须和结构体字段名一致,否则Scan不会赋值 -
聚合函数如
sum(amount)、count(*)要写在Select字符串里,不能靠 GORM 自动补全 - 若用
date(created_at)这类 MySQL 函数,注意不同数据库语法差异(PostgreSQL 用DATE(created_at))
结构体字段名必须匹配 SELECT 别名
很多人定义了 type Result { Date string `json:"date"` Total int } ,却在 Select 里写 "DATE(created_at) as day, sum(amount) as total",结果 Date 字段始终为空——因为 GORM 按字段名(Date)找别名(day),不匹配就跳过。
示例:
type DailySales struct {
Date string `gorm:"column:date"`
Total float64
}
var results []DailySales
db.Table("orders").
Select("date(created_at) as date, sum(amount) as total").
Group("date(created_at)").
Scan(&results)
这里 date 别名 → 结构体字段 Date,靠的是首字母大写 + 小写剩余部分自动映射(GORM 默认行为)。如果别名是 sale_date,就得加 tag:Date string `gorm:"column:sale_date"`。
Count 和 Sum 等聚合函数不能混用 Model 实例
db.Model(&User{}).Group("name").Count(&n) 这种写法看似合理,但 Count 返回的是整数总数,不是按组分片的结果;它底层执行的是 SELECT COUNT(*) FROM users GROUP BY name 的行数,而不是各组的 count 值。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
真正要获取每组记录数,必须走 Select + Group + Scan 流程:
- ❌ 错误:
db.Model(&User{}).Group("name").Count(&count)—— 得到的是分组后有多少组,不是每组数量 - ✅ 正确:
db.Table("users").Select("name, count(*) as total").Group("name").Scan(&results) - 若只想统计「满足某条件的组数」,用
db.Table(...).Group(...).Having(...).Count(&n),但注意这是最终组的数量,不是原始记录数
Having 条件必须用 Table + Raw 风格才稳定
Having 是对分组后结果的筛选,在 GORM v2 中,链式调用 Having 容易因语句顺序或内部 Clause 合并逻辑出错,尤其嵌套子查询或含聚合函数时。
推荐统一用 Table 开头 + 显式 Select + Having:
db.Table("orders").
Select("date(created_at) as date, sum(amount) as total").
Group("date(created_at)").
Having("sum(amount) > ?", 500).
Scan(&results)
避免用 Model(&Order{}).Having(...),因为 Model 可能注入额外 WHERE 或 JOIN,干扰 Having 所依赖的聚合上下文。另外,Having 中的字段必须是 Select 里出现过的聚合别名或原始列,不能是未出现在 SELECT 中的字段。
最常被忽略的一点:聚合字段类型必须和结构体字段类型一致。比如 sum(amount) 在 MySQL 中默认返回 DECIMAL,若结构体字段定义为 int,Scan 会静默失败(值为 0);应优先用 float64 或检查数据库实际返回类型。










