
GORM 原生不支持直接构建带 GROUP BY 的标量子查询作为 IN 条件,但可通过 Where("field IN (?)", subQuery) 组合 Session 或 Table() + Select() 构建子查询,避免硬编码 SQL,兼顾可读性与类型安全。
gorm 原生不支持直接构建带 group by 的标量子查询作为 in 条件,但可通过 `where("field in (?)", subquery)` 组合 `session` 或 `table()` + `select()` 构建子查询,避免硬编码 sql,兼顾可读性与类型安全。
在 GORM 中实现类似 SELECT * FROM MY_TABLE WHERE MY_FIELD IN (SELECT MAX(MY_FIELD) FROM MY_TABLE GROUP BY ANOTHER_FIELD) 的嵌套查询,无需使用 Raw SQL,关键在于将子查询作为 Where 方法的参数传入——GORM 会自动将其识别为子查询并正确拼接。
以下为推荐的、符合 GORM v2+ 最佳实践的写法(以 MY_TABLE 表为例,假设对应结构体为 MyModel):
type MyModel struct {
ID uint `gorm:"primaryKey"`
MyField int `gorm:"column:MY_FIELD"`
AnotherField string `gorm:"column:ANOTHER_FIELD"`
}
// 查询所有 MyField 等于各 AnotherField 分组下最大值的记录
func FindMaxByGroup(db *gorm.DB) ([]MyModel, error) {
var results []MyModel
// 构建子查询:SELECT MAX(MY_FIELD) FROM MY_TABLE GROUP BY ANOTHER_FIELD
subQuery := db.Table("MY_TABLE").
Select("MAX(MY_FIELD)").
Group("ANOTHER_FIELD")
// 主查询:WHERE MY_FIELD IN (子查询)
err := db.Where("MY_FIELD IN (?)", subQuery).
Find(&results).Error
return results, err
}
✅ 优势说明:
- ✅ 完全避免
db.Raw(),保留 GORM 的链式调用、日志追踪与方言适配能力; - ✅ 子查询由 GORM 自动处理别名与括号包裹,兼容 PostgreSQL、MySQL、SQLite;
- ✅ 可无缝结合
Joins、Preload、Scopes等高级特性。
⚠️ 注意事项:
- 子查询中
Select()必须仅返回单列(如MAX(MY_FIELD)),否则IN语义不成立,GORM 会报错或生成无效 SQL; - 若需关联主表字段(如
WHERE t1.id IN (SELECT t2.ref_id FROM t2 WHERE t2.status = ?)),建议用db.Session(&gorm.Session{NewDB: true})避免作用域污染; - GORM v2 默认启用
PrepareStmt: true时,部分复杂子查询可能触发预编译限制,可临时禁用:db.Session(&gorm.Session{PrepareStmt: false})。
? 进阶技巧:使用命名子查询提升可读性(GORM v2.2.5+)
sub := db.Table("MY_TABLE").Select("MAX(MY_FIELD)").Group("ANOTHER_FIELD")
db.Where("MY_FIELD IN ?", sub).Find(&results)
等价于原生 SQL,且 IDE 可跳转、单元测试易 Mock。
总结:GORM 的 Where("col IN (?)", subQuery) 是执行嵌套子查询的官方推荐方式。合理利用 Table()、Select() 和 Group() 即可安全、清晰地表达分组聚合后的过滤逻辑,无需牺牲抽象层优势。











