分组查询用于数据统计和分析。按某个字段分组,然后对每组进行聚合计算。
基本分组:
type Result struct {
Age int
Count int
}
var results []Result
db.Model(&User{}).Select("age, count(*) as count").Group("age").Find(&results)
生成的 SQL:
SELECT age, count(*) as count FROM users GROUP BY age
Having 过滤分组结果:
db.Model(&User{}).
Select("age, count(*) as count").
Group("age").
Having("count > ?", 1).
Find(&results)
生成的 SQL:
SELECT age, count(*) as count FROM users GROUP BY age HAVING count > 1
Count 计数
var count int64
db.Model(&User{}).Count(&count)
db.Model(&User{}).Where("age > ?", 20).Count(&count)
Sum 求和
var totalAge int
db.Model(&User{}).Select("sum(age)").Scan(&totalAge)
var totalAmount float64
db.Model(&Order{}).Select("sum(amount)").Scan(&totalAmount)
Avg 平均值
var avgAge float64
db.Model(&User{}).Select("avg(age)").Scan(&avgAge)
Max 最大值
var maxAge int
db.Model(&User{}).Select("max(age)").Scan(&maxAge)
Min 最小值
var minAge int
db.Model(&User{}).Select("min(age)").Scan(&minAge)
type Result struct {
Status string
Age int
Count int
}
var results []Result
db.Model(&User{}).
Select("status, age, count(*) as count").
Group("status, age").
Find(&results)
按状态统计用户:
type StatusCount struct {
Status string
Count int
}
var statusCounts []StatusCount
db.Model(&User{}).
Select("status, count(*) as count").
Group("status").
Find(&statusCounts)
按日期统计注册量:
type DailyCount struct {
Date string
Count int
}
var dailyCounts []DailyCount
db.Model(&User{}).
Select("DATE(created_at) as date, count(*) as count").
Where("created_at >= ?", time.Now().AddDate(0, -1, 0)).
Group("DATE(created_at)").
Order("date desc").
Find(&dailyCounts)
按分类统计文章:
type CategoryCount struct {
Category string
Count int
}
var categoryCounts []CategoryCount
db.Model(&Article{}).
Select("category, count(*) as count").
Group("category").
Order("count desc").
Find(&categoryCounts)
统计不重复的值:
var count int64
db.Model(&User{}).Distinct("name").Count(&count)
查询不重复的值:
var names []string
db.Model(&User{}).Distinct("name").Pluck("name", &names)
多个聚合函数:
type Stats struct {
Total int
AvgAge float64
MaxAge int
MinAge int
}
var stats Stats
db.Model(&User{}).
Select("count(*) as total, avg(age) as avg_age, max(age) as max_age, min(age) as min_age").
Scan(&stats)
条件聚合:
type Result struct {
Status string
Total int
ActiveCnt int
}
var results []Result
db.Model(&User{}).
Select("status, count(*) as total, sum(case when is_active = 1 then 1 else 0 end) as active_cnt").
Group("status").
Find(&results)
db.Model(&User{}).
Select("age, count(*) as count").
Group("age").
Order("count desc").
Find(&results)
db.Model(&User{}).
Select("age, count(*) as count").
Group("age").
Order("count desc").
Limit(5).
Find(&results)
分组统计关联数据:
type UserOrderCount struct {
UserName string
OrderCnt int
}
var results []UserOrderCount
db.Table("users").
Select("users.name, count(orders.id) as order_cnt").
Joins("left join orders on orders.user_id = users.id").
Group("users.id").
Find(&results)
字段不在 GROUP BY 中
db.Model(&User{}).Select("name, age, count(*)").Group("age")
某些数据库会报错,因为 name 不在 GROUP BY 中。MySQL 在严格模式下也会报错。
聚合结果为 NULL
没有匹配记录时,聚合函数返回 NULL:
var total *int
db.Model(&User{}).Where("age > ?", 100).Select("sum(age)").Scan(&total)
total 可能是 nil,要处理这种情况。
Count 与 Find 分开
var users []User
var count int64
db.Find(&users)
db.Count(&count)
两次查询,条件可能不一致。正确做法:
query := db.Model(&User{}).Where("status = ?", "active")
query.Count(&count)
query.Find(&users)
分组和聚合是数据分析的基础。掌握 Group、Having、各种聚合函数,能应对大部分统计需求。注意 SQL 标准对分组的要求,避免踩坑。