gorm join select 사용하기

joojong·2023년 1월 17일
0
post-thumbnail

새로운 프로젝트는 golang , python , mysql로 개발하고 있는데 gorm을 사용하면서 나름대로 정리한 글이다.

Model

type UserModel struct {
	ID           uuid.UUID       `gorm:"column:id;type:varchar(36);primary_key" json:"id"`
	Type         int8            `grom:"column:type;type:tinyint;default:0" json:"type"`
	NickName     string          `gorm:"column:nickname;type:varchar(30);default:null" json:"nickname"`
	Email        string          `gorm:"column:email;type:varchar(100);not null;unique" json:"email"`
	CreatedAt    time.Time       `gorm:"column:created_at;type:datetime;autoCreateTime" json:"created_at"`
	UpdatedAt    time.Time       `gorm:"column:updated_at;type:datetime;autoUpdateTime:milli" json:"updated_at"`
	DeletedAt    gorm.DeletedAt  `grom:"column:deleted_at;type:datetime;" json:"deleted_at"`
}

우선 위와 같은 UserModel이 있다고 생각하자.

Select

var model []models.UserModel
database.DB.Find(&model)

위와 같은 쿼리를 날리면

[83.010ms] [rows:1] SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL

이런 식으로 테이블에 있는 전체 컬럼을 가지고 오게된다.
테이블에 담긴 데이터가 작을 떄는 큰문제가 되지 않지만 row가 많아졌을 때는 성능차이가 생각보다 많이 난다.


지금 개발하고 있는 테이블에서 전체 row를 가지고 왔을 때 50만개 정도의 row가 있고
전체 컬럼을 가지고 왔을때 8.8초
하나의 컬럼만 가지고 왔을 때는 1.9초가 걸린다.
물론 퍼포먼스는 매번 다르겠지만 전체 컬럼을 가지고 왔을 때와 특정 컬럼만 가지고 왔을 때의 차이는 꽤 많이 난다.

방법1

var model []model.UserModel
database.DB.Select("email", "nickname").Find(&model)
return &model

위와 같은 방법으로 특정 컬럼을 가지고 올 수 있다

[6.622ms] [rows:1] SELECT `email`,`nickname` FROM `users` WHERE `users`.`deleted_at` IS NULL

집계함수를 사용할 때는 위와 같이 사용할 수 밖에 없어보이지만 그게 아니라면?

방법2

// 특정 컬럼을 가지고 올 구조체를 만들어준다.
type SpecificColumsUserModel struct {
	NickName string `gorm:"column:nickname;type:varchar(30);default:null" json:"nickname"`
	Email    string `gorm:"column:email;type:varchar(100);not null;unique" json:"email"`
}

var model []SpecificColumsUserModel
// Model에 select할 테이블을 명시해준다.
database.DB.Model(&models.UserModel{}).Find(&model)
return &model
[11.377ms] [rows:1] SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL

위와 같이 새로운 구조체를 만들어서 쿼리를 해도 똑같은 결과가 나온다.

선호하는 방식에서는 차이가 있을 수 있는데 2주정도 golang을 해보면서 느낀점은 두번째 방법이 더 적절하다고 느껴진다.

join

위에 설명한 UserModel은 DeviceModel을 여러개를 가지고 있을 수 있다고 생각해보자.


UserModel

type UserModel struct {
	ID           uuid.UUID       `gorm:"column:id;type:varchar(36);primary_key" json:"id"`
	Type         int8            `grom:"column:type;type:tinyint;default:0" json:"type"`
	NickName     string          `gorm:"column:nickname;type:varchar(30);default:null" json:"nickname"`
	Email        string          `gorm:"column:email;type:varchar(100);not null;unique" json:"email"`
	CreatedAt    time.Time       `gorm:"column:created_at;type:datetime;autoCreateTime" json:"created_at"`
	UpdatedAt    time.Time       `gorm:"column:updated_at;type:datetime;autoUpdateTime:milli" json:"updated_at"`
	DeletedAt    gorm.DeletedAt  `grom:"column:deleted_at;type:datetime;" json:"deleted_at"`
    Devices      []DeviceModel   `gorm:"foreignKey:UserId" json:"devices"`
}

DeviceModel

type DeviceModel struct {
    // UserModel에서 정해준 foreignKey는 DeviceModel.UserId가 된다
	UserId       uuid.UUID `gorm:"column:user_id;type:varchar(36);primary_key" json:"-"`
	User         UserModel
    DeviceName   string    `gorm:"column:device_name;type:varchar(50);default:null" json:"device_name"`
	FcmToken     string    `gorm:"column:fcm_token;type:varchar(255);default:null" json:"fcm_token"`
}

gorm문서에서 설명하듯

db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id

저렇게 조인할 수도 있지만 Preload로 똑같은 결과값을 구할 수 있다.

var user models.UserModel
database.DB.Model(&models.UserModel{}).Preload(
	var devices []models.DeviceModel
	// join할 키 값
	"Devices", func(device *gorm.DB) *gorm.DB {
	return device.Find(&devices{})
}).Find(&user)

위와 같이 쿼리를 날릴 수도 있는데 어떤 방식이 맞는거냐라고 물어보면 선호하는 방식에 차이가 있을 수 있을 것 같다.

type SpecificColumsUserModel struct {
	NickName string          `gorm:"column:nickname;type:varchar(30);default:null" json:"nickname"`
	Email    string          `gorm:"column:email;type:varchar(100);not null;unique" json:"email"`
    Devices  []DeviceModel   `gorm:"foreignKey:UserId" json:"devices"`
}
type SpecificColumnsDeviceModel struct {
	UserId       uuid.UUID `gorm:"column:user_id;type:varchar(36);primary_key" json:"-"`
	User         UserModel
    DeviceName   string    `gorm:"column:device_name;type:varchar(50);default:null" json:"device_name"`
}
var user SpecificColumsUserModel
database.DB.Model(&models.UserModel{}).Preload(
	var devices []SpecificColumnsDeviceModel
	// join할 키 값
	"Devices", func(device *gorm.DB) *gorm.DB {
	return device.Model(&models.DeviceModel{}).Find(&devices{})
}).Find(&user)

join에서 셀렉하는 방법은 기존에 셀렉하는 방법과 크게 다를게 없다. Model에서 셀렉할 테이블을 명시해주고 Find 파라미터에 특정 컬럼만을 셀렉할 구조체를 넣어주면 된다.

profile
Back-end developer

0개의 댓글