GORM(Golang)中如何实现带Where、Order By的多表Union查询
问题原因
原有写法不生效的核心逻辑:GORM 不会自动将? UNION ?拼接后的整体结果包装为派生表,后续链式调用的Where、Order方法无法识别Raw语句内的UNION结构,这部分筛选、排序条件会被直接丢弃,不会拼接到最终执行的SQL中。
要实现对UNION合并后结果集的过滤、排序,必须把UNION查询整体作为子查询,在外层包裹查询语句执行条件筛选。
可直接运行的实现方案
方案1:通用兼容方案(所有GORM v2版本可用)
先分别构建两个单表的查询语句,将拼接好的UNION语句作为派生表,在外层执行后续的过滤、排序逻辑:
var union []map[string]interface{} // 构建两个分支的子查询,不要提前执行Scan q1 := database.CONNECTION.Select(ContentAttributes).Model(&model1{}) q2 := database.CONNECTION.Select(ContentAttributes).Model(&model2{}) // 将UNION结果作为临时表,外层追加查询条件 database.CONNECTION.Table("(?) AS union_temp", database.CONNECTION.Raw("? UNION ?", q1, q2), ).Where("id > ?", 1).Order("name").Scan(&union)
该写法生成的SQL结构完全符合预期:
SELECT * FROM ( SELECT "id","name","created_at","updated_at" FROM "model1" WHERE "model1"."deleted_at" IS NULL UNION SELECT "id","name","created_at","updated_at" FROM "model2" WHERE "model2"."deleted_at" IS NULL ) AS union_temp WHERE id > 1 ORDER BY name
方案2:高版本GORM简化写法(v1.23.0及以上版本支持)
如果项目使用的GORM版本较新,可以直接用内置的Union方法构建组合查询,写法更简洁:
var union []map[string]interface{} // 用内置API构建UNION组合查询 unionQuery := database.CONNECTION.Select(ContentAttributes).Model(&model1{}). Union(database.CONNECTION.Select(ContentAttributes).Model(&model2{})) // 基于UNION结果追加筛选、排序逻辑 database.CONNECTION.Table("(?) AS t", unionQuery). Where("id > ?", 1). Order("name"). Scan(&union)
注意事项
- 参与UNION的两个分支查询,返回字段的数量、顺序、对应字段类型必须完全一致,否则会触发SQL语法错误
- 如果不需要对合并结果去重,将
UNION替换为UNION ALL即可,查询性能会有明显提升 - 排序如果需要指定升降序,可以直接写为
Order("name desc")、Order("id asc")这类标准SQL格式
内容的提问来源于stack exchange,提问作者Rajesh Paul
相关产品推荐
相关产品推荐

