gorm中的高級查詢


智能選擇字段

GORM 允許通過 Select 方法選擇特定的字段,如果您在應用程序中經常使用此功能,你也可以定義一個較小的結構體,以實現調用 API 時自動選擇特定的字段,例如:

type User struct {
ID int `gorm:"primaryKey;autoIncrement"`
Name sql.NullString `gorm:"default:'隔壁老王'"`
Age uint8 `gorm:"default:55"`
UUID uuid.UUID
CreatedAt time.Time `gorm:"autoCreateTime"`
}
type APIUser struct {
ID int
Name string
}
// 查詢時會自動選擇ID、Name字段
db.Debug().Model(new(User)).Find(&apiUsers) // SELECT `users`.`id`,`users`.`name` FROM `users`

 注意 QueryFields 模式會根據當前 model 的所有字段名稱進行 select。

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
  QueryFields: true,
})
db.Debug().Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`users`.`uuid`,`users`.`created_at` FROM `users`


// Session Mode
db.Debug().Session(&gorm.Session{QueryFields: true}).Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`users`.`uuid`,`users`.`created_at` FROM `users`

  

Locking (FOR UPDATE)

GORM 支持多種類型的鎖,例如:

// 獨占鎖
db.Debug().Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
// SELECT * FROM `users` FOR UPDATE

// 共享鎖
db.Debug().Clauses(clause.Locking{
Strength: "SHARE",
Table: clause.Table{Name: clause.CurrentTable},
}).Find(&users)
// SELECT * FROM `users` FOR SHARE OF `users`

  

子查詢

  子查詢可以嵌套在查詢中,GORM 允許在使用 *gorm.DB 對象作為參數時生成子查詢

db.Debug().Where("age > (?)", db.Table("users").Select("avg(age)")).Find(&users)
// SELECT * FROM `users` WHERE age > (SELECT avg(age) FROM `users`)

// 智能選擇字段
type APIUser struct {
Name string
Age uint8
}
// 子查詢
var users []APIUser
subQuery := db.Debug().Table("users").Select("avg(age) as age").Where("name IN (?)", []string{"李四", "趙六"})
db.Debug().Model(&User{}).Where("age > (?)", subQuery).Scan(&users)
// SELECT * FROM `users` WHERE age > (SELECT avg(age) as age FROM `users` WHERE name IN ('李四','趙六'))

  

From 子查詢

  GORM 允許您在 Table 方法中通過 FROM 子句使用子查詢,例如:

db.Debug().Table("(?) as u", db.Model(new(User)).Select("name, age")).Where("age > ?", 18).Scan(&users)
// SELECT * FROM (SELECT name, age FROM `users`) as u WHERE age > 18

subQuery1 := db.Model(new(User)).Select("name")
subQuery2 := db.Model(&Email{}).Select("email")
db.Debug().Table("(?) as a, (?) as b", subQuery1, subQuery2).Scan(&users)
// SELECT * FROM (SELECT `name` FROM `users`) as a, (SELECT `email` FROM `emails`) as b // 生成笛卡爾積表

  

Group 條件

  使用 Group 條件可以更輕松的編寫復雜 SQL

db.Debug().Model(new(User)).Where(
  db.Where("name <> ?", "老王").Where(db.Where("age = ?", 10).Or("age = ?", 11)),
).Or(
  db.Where("name <> ?", "李四").Where("age = ?", 55),
).Scan(&users)
// SELECT * FROM `users` WHERE (name <> '老王' AND (age = 10 OR age = 11)) OR (name <> '李四' AND age = 55)

  

命名參數

  GORM 支持 sql.Named 和 map[string]interface{}{} 形式的命名參數,例如:

db.Debug().Model(&User{}).Where(
  "name = @name OR age = @age", sql.Named("name", "李四"), sql.Named("age", 11),
).Scan(&apiUsers)
// SELECT * FROM `users` WHERE name = '李四' OR age = 11

db.Debug().Model(new(User)).Where(
"name=@n", map[string]interface{}{"n": "老王"},
).Find(&apiUsers)
// SELECT `users`.`name`,`users`.`age` FROM `users` WHERE name='老王'

  

Find 至 map

  GORM 允許掃描結果至 map[string]interface{} 或 []map[string]interface{},此時別忘了指定 Model 或 Table,例如:

var m1 map[string]interface{}
db.Debug().Model(new(User)).First(&m1, "id = ?", 5)
// SELECT * FROM `users` WHERE id = 5 ORDER BY `users`.`id` LIMIT 1

var m1 []map[string]interface{}
db.Debug().Model(new(User)).Find(&m1)
// SELECT * FROM `users`

  

FirstOrInit

  獲取第一條匹配的記錄,或者根據給定的條件初始化一個實例(僅支持 sturct 和 map 條件)

// 未找到user,根據給定的條件初始化 struct
db.Debug().FirstOrInit(&user, &User{Name: sql.NullString{"老王2", true}})
// SELECT * FROM `users` WHERE `users`.`name` = '老王2' ORDER BY `users`.`id` LIMIT 1
// 找到了user
db.Debug().Where(&User{Age: 10}).FirstOrInit(&user)
// SELECT * FROM `users` WHERE `users`.`age` = 10 ORDER BY `users`.`id` LIMIT 1

// 使用map
db.Debug().FirstOrInit(&user, map[string]interface{}{"name": "李四"})
// SELECT * FROM `users` WHERE `name` = '李四2' ORDER BY `users`.`id` LIMIT 1

  如果沒有找到記錄,可以使用包含更多的屬性的結構體初始化 user,Attrs 不會被用於生成查詢 SQL

// 未找到user,則根據給定的條件和Attrs初始化user
db.Debug().Where(&User{Name: sql.NullString{"趙武", true}}).Attrs(&User{Age: 99}).FirstOrInit(&user)
// SELECT * FROM `users` WHERE `users`.`name` = '趙武' ORDER BY `users`.`id` LIMIT 1

// 找到了name=李四的user,則忽略:Attrs
db.Debug().Where("name = ?", "李四").Attrs(User{Age: 88}).FirstOrInit(&user)
// SELECT * FROM `users` WHERE name = '李四' ORDER BY `users`.`id` LIMIT 1

  不管是否找到記錄,Assign 都會將屬性賦值給 struct,但這些屬性不會被用於生成查詢 SQL,也不會被保存到數據庫

// 未找到user,根據條件和Assign初始化struct
db.Debug().Where(User{Name: sql.NullString{"NonUser", true}}).Assign(User{Age: 5}).FirstOrInit(&user)
// SELECT * FROM `users` WHERE `users`.`name` = 'NonUser' ORDER BY `users`.`id` LIMIT 1
// 找到name=李四的user, 依然會更新Assign屬性
db.Debug().Where(User{Name: sql.NullString{"老王", true}}).Assign(User{Age: 5}).FirstOrInit(&user)
// SELECT * FROM `users` WHERE `users`.`name` = '老王' ORDER BY `users`.`id` LIMIT 1

  

FirstOrCreate

  獲取第一條匹配的記錄,或者根據給定的條件創建一條新紀錄(僅支持 sturct 和 map 條件)

// 未找到user,則根據給定的條件創建一條新紀錄
db.Debug().Attrs(User{Name: sql.NullString{"馬艷娜", true}}).FirstOrCreate(&user, User{Age: 12})
// SELECT * FROM `users` WHERE `users`.`age` = 12 ORDER BY `users`.`id` LIMIT 1
// INSERT INTO `users` (`name`,`age`,`uuid`,`created_at`) VALUES ('馬艷娜',12,'00000000-0000-0000-0000-000000000000','2021-11-19 10:04:54.991')

// 找到了Age = 12的user,同時也把Assign屬性賦值給age,更新數據庫的Assign屬性
db.Debug().Where(User{Age: 12}).Assign(map[string]interface{}{
"name": "馬亞南",
}).FirstOrCreate(&user)
// SELECT * FROM `users` WHERE `users`.`age` = 12 ORDER BY `users`.`id` LIMIT 1
// UPDATE `users` SET `name`='馬亞南' WHERE `users`.`age` = 12 AND `id` = 6 ORDER BY `users`.`id` LIMIT 1

  如果沒有找到記錄,可以使用包含更多的屬性的結構體創建記錄,Attrs 不會被用於生成查詢 SQL 。

  不管是否找到記錄,Assign 都會將屬性賦值給 struct,並將結果寫回數據庫

  未找到 user,根據條件和 Attrs 屬性創建記錄, 找到了 `name` = `jinzhu` 的 user,則忽略 Attrs

  未找到 user,根據條件和 Assign 屬性創建記錄,  找到了 `name` = `jinzhu` 的 user,依然會根據 Assign 更新記錄

迭代

  GORM 支持通過行進行迭代

// ScanRows方法用於將一行Rows掃描至結構體
rows, _ := db.Model(&User{}).Rows()
for rows.Next() {
var user User
db.ScanRows(rows, &user)
myPrint(user)
}

  

FindInBatches

  用於批量查詢並處理記錄

var users []User
db.Debug().FindInBatches(&users, 3, func(tx *gorm.DB, batch int) error {
for _, result := range users {
spew.Println(result)
}
spew.Println(tx.RowsAffected) // 本次批量操作影響的記錄數
return nil
})
/*
SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 3
5是根據上次查詢的最后一個ID確定的
SELECT * FROM `users` WHERE `users`.`id` > 5 ORDER BY `users`.`id` LIMIT 3
*/

  

查詢鈎子

  對於查詢操作,GORM 支持 AfterFind 鈎子,查詢記錄后會調用它

func (u *User) AfterFind(tx *gorm.DB) (err error) {
// 查詢之后會調用的鈎子
if u.Age > 20 {
spew.Println("您的歲數是%d,超過20歲了", u.Age)
}
return
}

  

Pluck

  Pluck 用於從數據庫查詢單個列,並將結果掃描到切片。如果您想要查詢多列,您應該使用 Select 和 Scan

var ages []int
db.Debug().Table("users").Pluck("age", &ages)
// SELECT `age` FROM `users`

var names []string
db.Debug().Model(&User{}).Distinct().Pluck("name", &names)
// SELECT DISTINCT `name` FROM `users`

// Distinct Select
var users []APIUser
db.Debug().Model(&User{}).Distinct().Select("name, age").Scan(&users)
// SELECT distinct name, age FROM `users`

// 超過一列的查詢,應該使用 `Scan` 或者 `Find`,例如:
db.Select("name", "age").Scan(&users)
db.Select("name", "age").Find(&users)

  

Scopes

  Scopes允許你指定常用的查詢,可以在調用方法時引用這些查詢

func LaoWang(db *gorm.DB) *gorm.DB {
return db.Where("name = ?", "老王")
}
func Age10(db *gorm.DB) *gorm.DB {
return db.Where(map[string]interface{}{"age": 10})
}
// Scopes允許你指定常用的查詢,可以在調用方法時引用這些查詢
var users []User
db.Debug().Scopes(LaoWang, Age10).Find(&users) // 查詢name=老王而且age=10的users
// SELECT * FROM `users` WHERE name = '老王' AND `age` = 10


func Age10Or11(age []int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("age IN (?)", age)
}
}
db.Debug().Scopes(Age10Or11([]int{10, 11})).Find(&users)  // 查詢age = 10或age=11的users
// SELECT * FROM `users` WHERE age IN (10,11)

  

Count

  Count 用於獲取匹配的記錄數

var count int64
db.Debug().Model(new(User)).Count(&count)
// SELECT count(*) FROM `users`

var count int64
db.Debug().Table("users").Distinct("name").Count(&count)
// SELECT COUNT(DISTINCT(`name`)) FROM `users`

var count int64
db.Debug().Model(&User{}).Select("count(distinct(name))").Count(&count)
// SELECT count(distinct(name)) FROM `users`

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM