如何用GORM实现含UNION子查询的SQL?是否只能用Raw()?
GORM实现目标UNION ALL查询方案
问题分析
你的目标SQL里,两个UNION ALL的子查询逻辑是分开的:第一个只需要从models里展开tags,第二个需要关联projects并筛选公开项目后展开system_tags。你之前把整个UNION语句塞到Select里还加了全局的Joins,这会导致第一个子查询也被错误关联,不符合需求。
方案一:用GORM原生Union方法(推荐,GORM v2+支持)
GORM v2提供了Union和UnionAll方法,可以分别构建两个子查询再合并,完美匹配你的需求:
// 定义对应表的结构体示例 type Model struct { ID uint Tags []string `gorm:"type:text[]"` // 对应tags数组字段 SystemTags []string `gorm:"type:text[]"` // 对应system_tags数组字段 ProjectID uint } type Project struct { ID uint IsPublic bool } // 构建第一个子查询:展开tags字段 subQuery1 := db.Model(&Model{}).Select("unnest(tags) as tag_name") // 构建第二个子查询:关联projects并筛选公开项目,展开system_tags subQuery2 := db.Model(&Model{}). Select("unnest(system_tags) as tag_name"). Joins("left join projects on projects.id = models.project_id"). Where("projects.is_public = ?", true) // 用UNION ALL合并两个子查询 unionSubQuery := subQuery1.UnionAll(subQuery2) // 外层查询:筛选tag_name包含"12"(小写匹配)并排序 var tagNames []string err := db.Model(&Model{}). Select("tag_name"). From("(?) as tD", unionSubQuery). // 将合并后的子查询作为临时表tD Where("LOWER(tD.tag_name) LIKE ?", "%12%"). Order("tag_name"). Find(&tagNames).Error if err != nil { // 处理错误逻辑 }
方案二:使用Raw方法(兼容所有GORM版本)
如果你的GORM版本较低不支持Union方法,或者觉得子查询构建太繁琐,可以直接用Raw方法,但一定要用参数化查询避免SQL注入:
var tagNames []string sql := ` SELECT tag_name FROM ( SELECT unnest(tags) as tag_name FROM models UNION ALL SELECT unnest(system_tags) from models LEFT JOIN projects on projects.id = models.project_id WHERE projects.is_public = true ) as tD WHERE LOWER(tD.tag_name) LIKE ? ORDER BY tag_name ` err := db.Raw(sql, "%12%").Scan(&tagNames).Error
关键注意点
- 不要把
Joins和Where加在整个UNION查询上,要确保只有第二个子查询包含关联和筛选逻辑 - 如果用Raw方法,务必通过
?占位符传递参数,不要直接拼接字符串,防止SQL注入
内容的提问来源于stack exchange,提问作者Howkee
相关产品推荐
相关产品推荐

