Golang Gorm 同时使用 Preload 和 Left Join 进行联表查询并过滤数据

更新日期: 2025-11-25 阅读次数: 28 字数: 488 分类: golang

需求背景

我有两个 MySQL 数据表:

  • 员工表 staff
  • 薪资表 salary。这里有员工 ID 字段作为外键关联员工表。

我定义的薪资 Struct 结构体,包含了 embedded 字段 Staff,用于关联员工信息:

type Salary struct {
	gorm.Model
	StaffId uint
	Staff   Staff `gorm:"foreignKey:StaffId"`
	Amount  float64
}

我需要在返回薪资信息列表时,同时返回员工的详细信息(通过 Preload 预加载实现),并且根据员工的某些字段进行过滤(通过 Left Join 实现)。

golang 代码

下面是一个示例代码,展示了如何使用 Gorm 的 Preload 和 Left Join 来实现这个需求:

func GetSalaryList(c *gin.Context) {
	var items []Salary
	db := models.DB.Model(&Salary{}).
		Select("salary.*").
		Joins("LEFT JOIN staff ON salary.staff_id = staff.id") // 使用 Left Join 关联员工表

	// 根据员工姓名进行过滤
	if name, isExist := c.GetQuery("name"); isExist {
		db = db.Where("staff.name LIKE ?", fmt.Sprintf("%%%s%%", name))
	}

	// After a Chain method, Finisher Method, GORM returns an initialized
	// *gorm.DB instance, which is NOT safe to reuse anymore, or new generated
	// SQL might be polluted by the previous conditions.
	// In order to reuse a initialized *gorm.DB instance, you can use a
	// New Session Method to create a shareable *gorm.DB
	db = db.Session(&gorm.Session{})

	var count int64 = 0
	db.Distinct("salary.id").Count(&count)

	db.Preload("Staff"). // 预加载 Staff 关联
		Order("salary.id desc").
		Limit(limit).
		Offset((page - 1) * limit).
		Find(&items)
}

需要注意的两个地方:

  1. Preload 需要在 count 统计之后使用,因为 count 统计时不需要预加载数据。也可能会出现 count 返回 0 的问题。
  2. count 加上 Distinct("salary.id"),避免因为 Left Join 导致的重复记录计数问题。

我发现 VSCode 里的 Github Copilot 的 GPT-5 mini 模型,在有项目代码上下文的情况下,比 DeepSeek 强太多了。 不用去解释过多的前提。所以网页版的 AI 工具,效率还是差一些。

关于作者 🌱

我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式