Beego ORM 的 Distinct 方法仅支持单字段去重,多字段会静默忽略或报错;需用 Raw() 执行原生 SELECT DISTINCT 多字段 SQL 或结合 GROUP BY 实现组合去重。

Beego ORM 的 Distinct 方法不支持多字段去重
Beego ORM 的 QuerySeter.Distinct() 只能对单个字段生效,且底层生成的 SQL 是 SELECT DISTINCT field FROM ...。如果你传入多个字段(比如 Distinct("Name", "Age")),它会静默忽略后续字段,只保留第一个——这不是 bug,是设计限制。
常见错误现象:代码写了 qs.Distinct("Name", "Email"),结果查出来的记录数没变,甚至报错 invalid number of arguments(取决于 Beego 版本)。
- Beego 1.x 中
Distinct()最多接受 1 个参数,多传会 panic 或被截断 - 若需多字段组合去重(如“姓名+邮箱”唯一),必须改用
Raw()或手动拼GROUP BY - 注意:MySQL 5.7+ 默认
sql_mode含ONLY_FULL_GROUP_BY,直接GROUP BY a,b后选其他非分组字段会报错
用 Raw() 手写 DISTINCT 多字段查询最可靠
绕过 ORM 的字段限制,直接发原生 SQL 是最可控的方式。尤其适合导出、统计、后台管理等非高频读场景。
示例:查用户表中不重复的 Name 和 Email 组合:
var users []map[string]interface{}
o.Raw("SELECT DISTINCT `name`, `email` FROM user ORDER BY `name`").Values(&users)
-
Values()自动映射为map[string]interface{},字段名按数据库小写(注意反引号包裹) - 如果想映射到结构体,得用
QueryRows(&users)+ 自定义 struct(字段名需与 SELECT 列完全一致) - WHERE 条件要手写进 SQL 字符串,别漏了 SQL 注入防护——优先用
o.Raw(sql, args...)占位符方式
GROUP BY 替代 DISTINCT 的适用边界
当你要去重的同时取某条“最新记录”(比如每个邮箱对应的最新注册用户),GROUP BY 比 DISTINCT 更有用,但 Beego ORM 不提供原生 GROUP BY 链式调用。
正确做法是结合 Raw() 和子查询或窗口函数(MySQL 8.0+/PostgreSQL):
o.Raw(`
SELECT u1.* FROM user u1
INNER JOIN (
SELECT email, MAX(id) as max_id FROM user GROUP BY email
) u2 ON u1.email = u2.email AND u1.id = u2.max_id
`).All(&users)
- 避免在
GROUP BY后直接SELECT *,MySQL 5.7+ 会拒绝(除非关掉ONLY_FULL_GROUP_BY) - Beego 的
All()要求 struct 字段与 SELECT 列严格匹配,列别名也要对应 - 性能上,
GROUP BY在大数据量时比DISTINCT更吃索引,记得给(email, id)加联合索引
Beego 2.x 的 QueryBuilder 仍不支持 DISTINCT 多字段
即使升级到 Beego 2.x,orm.NewQueryBuilder("mysql") 生成的 DISTINCT 语句依然只接受单字段。它的 Build() 方法最终还是调用同名 ORM 方法,没突破底层限制。
所以别寄希望于换版本自动解决——只要 ORM 层没暴露多字段 distinct 接口,就得回到 Raw() 或提前在数据库建好物化视图/唯一索引约束。
真正容易被忽略的点:去重逻辑如果放在应用层(比如查全量再用 map 去重),内存和网络开销可能远超 SQL 层去重;而误信 ORM 文档里 “支持 Distinct” 的模糊描述,最容易在上线后才发现数据不对。











