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

文章目录

    需求背景

    我有两个 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 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式