You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在GORM中仅返回满足预加载条件的关联非空记录?

问题描述

现有一对多关联的两张表(User和Email),使用GORM操作数据库时,当前通过Preload("Emails", "address like 'user_1@%'")查询后,会返回所有用户——符合邮箱条件的用户会加载对应的Emails,不符合的用户Emails字段为空。现在希望直接通过GORM实现筛选,只返回符合Preload中指定邮箱条件的用户,而非后续在内存中过滤。

示例代码

package main

import (
    "github.com/davecgh/go-spew/spew"
    "github.com/gofrs/uuid"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/clause"
)

type Email struct {
    ID      uuid.UUID `gorm:"primary_key;type:char(36);"`
    Address string    `gorm:"unique"`
    Uid     uuid.UUID
}

type User struct {
    ID     uuid.UUID `gorm:"primary_key;type:char(36);"`
    Name   string    `gorm:"unique"`
    Emails []Email   `gorm:"ForeignKey:Uid"`
}

func main() {
    var err error
    var db *gorm.DB
    db, err = gorm.Open(mysql.New(mysql.Config{
        DSN: "root:AsdfgH123@tcp(localhost:3306)/maindb?charset=utf8&parseTime=True&loc=Local",
    }), &gorm.Config{})
    if err != nil {
        panic(err)
    }
    err = db.AutoMigrate(&User{}, &Email{})
    if err != nil {
        panic(err)
    }

    uid1, _ := uuid.NewV7()
    uid2, _ := uuid.NewV7()
    eid1, _ := uuid.NewV7()
    eid2, _ := uuid.NewV7()

    users := []User{
        {Name: "user_1", ID: uid1, Emails: []Email{{ID: eid1, Address: "user_1@example.com"}}},
        {Name: "user_2", ID: uid2, Emails: []Email{{ID: eid2, Address: "user_2@example.com"}}},
    }
    db.Clauses(clause.Insert{Modifier: "IGNORE"}).Create(&users)
    dbUsers := []User{}

    db.Preload("Emails", "address like 'user_1@%'").Find(&dbUsers)

    spew.Dump(dbUsers)
}

执行结果

([]main.User) (len=2 cap=20) {
 (main.User) {
  ID: (uuid.UUID) (len=16 cap=16) 01833180-a4f4-7663-a73e-798b110724ed,
  Name: (string) (len=6) "user_1",
  Emails: ([]main.Email) (len=1 cap=10) {
   (main.Email) {
    ID: (uuid.UUID) (len=16 cap=16) 01833180-a4f4-70e6-ad25-c5726212a97c,
    Address: (string) (len=18) "user_1@example.com",
    Uid: (uuid.UUID) (len=16 cap=16) 01833180-a4f4-7663-a73e-798b110724ed
   }
  }
 },
 (main.User) {
  ID: (uuid.UUID) (len=16 cap=16) 01833180-a4f4-738f-ad46-1c7bb36cf79c,
  Name: (string) (len=6) "user_2",
  Emails: ([]main.Email) (cap=10) {
  }
 }
}

需要解决的核心问题:能否直接通过GORM实现筛选,仅返回符合Preload中指定邮箱条件的用户?


解决方案

可以实现,Preload仅负责关联数据的预加载,不会过滤主表数据。要只返回符合邮箱条件的用户,需要结合关联查询逻辑过滤主表,同时保留预加载,具体有两种常用方式:

方式一:使用Joins关联过滤

通过关联Email表,直接在主查询中筛选出拥有符合条件邮箱的用户,再预加载对应邮箱数据:

dbUsers := []User{}
db.Joins("JOIN emails ON users.id = emails.uid AND emails.address LIKE ?", "user_1@%").
    Preload("Emails", "address LIKE ?", "user_1@%").
    Find(&dbUsers)
spew.Dump(dbUsers)

该方式会生成关联查询,主查询仅返回符合条件的用户,同时预加载对应的邮箱数据。

方式二:使用EXISTS子查询过滤

利用Where结合EXISTS子查询,筛选出存在符合条件邮箱的用户,再预加载对应数据:

dbUsers := []User{}
db.Preload("Emails", "address LIKE ?", "user_1@%").
    Where("EXISTS (SELECT 1 FROM emails WHERE emails.uid = users.id AND emails.address LIKE ?)", "user_1@%").
    Find(&dbUsers)
spew.Dump(dbUsers)

这种方式通过子查询判断用户是否存在符合条件的邮箱,仅保留符合条件的用户,同时预加载对应邮箱数据。

注意事项

  • 两种方式生成的SQL不同,可根据业务场景选择;
  • 必须使用参数占位符?绑定条件值,避免SQL注入风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 18:05:28