You need to enable JavaScript to run this app. 如何在GORM中实现MySQL UNION ALL联合查询
优惠活动
大模型
产品
解决方案
定价
更多

如何在GORM中实现MySQL UNION ALL联合查询

GORM 实现 UNION ALL 查询的最佳实践

GORM v2 原生内置Union、UnionAll方法,完全可以通过链式API组合出等价于原生SQL的UNION ALL逻辑,迁移存量查询时优先选择可维护性更高的链式写法,逻辑过度复杂时再退回Raw模式,全程要和原有原生SQL的行为做一致性校验,避免迁移后结果偏差。

标准实现方式

对应你给出的原有SQL,无需硬拼字符串即可实现等价查询:

// 因两张表UNION查询的列对齐,可复用现有模型作为结果接收结构体
var unionResults []model.Foo

// 拼接UNION ALL逻辑,生成等价SQL:SELECT * FROM foo UNION ALL SELECT * FROM bar WHERE id = 1
err := db.Model(&model.Foo{}).
    Unscoped(). // 关闭GORM默认的软删除过滤,和原SQL的全量查询行为对齐
    Session(&gorm.Session{}). // 隔离会话,避免链式条件污染其他子查询
    UnionAll(
        db.Model(&model.Bar{}).
            Unscoped(). // 同步关闭软删除过滤
            Where("id = ?", 1). // 用占位符传参,避免SQL注入
            Session(&gorm.Session{}),
    ).
    Scan(&unionResults).Error

这种写法可以直接复用GORM的结果映射、参数转义能力,比原生db.Query手动遍历行赋值的维护成本低很多。

核心实践原则

  • *不要无依赖使用SELECT 做UNION查询
    生产环境哪怕两张表当前结构完全一致,后续任意一张表调整字段顺序、新增/删除列,都会导致UNION查询出现列数不匹配、结果错位的问题。最佳实践是显式列出查询列,给所有列设置完全一致的别名,必要时做类型强转匹配前后列的数据类型。
  • 主动对齐GORM和原生SQL的行为差异
    GORM默认会给所有带gorm.DeletedAt字段的模型自动附加deleted_at IS NULL的软删除过滤条件,如果原有原生SQL是无过滤的全量查询,必须在子查询中调用Unscoped()方法,否则会出现结果集比原SQL少数据的迁移问题。
  • 复杂逻辑可灵活退回Raw模式
    如果UNION逻辑嵌套层级过多、附带复杂的排序/聚合/分页逻辑,链式构建的代码冗余度过高时,可以直接使用GORM的Raw方法执行原生SQL,依然要通过占位符传递参数,配合Scan完成结果映射,相比裸用db.Query的写法优势更明显。
    对应你原有SQL的Raw改造示例:
query := `
  SELECT * FROM foo
  UNION ALL
  SELECT * FROM bar WHERE id = ?`
var unionResults []model.Foo
err := db.Raw(query, 1).Scan(&unionResults).Error

常见踩坑点

  • 嵌套UNION结果集时忘记加表别名:如果需要把UNION返回的结果作为派生表,关联其他表、做聚合计算、嵌套到其他查询逻辑中,必须给UNION派生表设置别名(例如db.Table("(?) AS union_res", 你的UNION链式查询)),否则会触发数据库语法错误。如果只是单纯查询UNION结果、追加全局排序/分页,完全可以直接链式调用Scan,不需要额外套外层表。
  • 忽略子查询列顺序:哪怕两个子查询的列名完全一致,只要列的返回顺序不同,GORM扫描结果时就会出现字段值错位的问题。
  • 未隔离子查询会话:构建多个子查询时,每个子查询都要单独加Session(&gorm.Session{}),避免GORM的链式条件意外污染到其他子查询,比如过滤条件被重复拼接到两个子查询上。

内容的提问来源于stack exchange,提问作者Muslihudin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.24 11:54:03