教程 > gorm 教程 > 阅读:146

gorm 高级查询——迹忆客-ag捕鱼王app官网

智能选择字段

gorm 允许通过 select 方法选择特定的字段,如果您在应用程序中经常使用此功能,你也可以定义一个较小的结构体,以实现调用 api 时自动选择特定的字段,例如:

type user struct {
  id     uint
  name   string
  age    int
  gender string
  // 假设后面还有几百个字段...
}
type apiuser struct {
  id   uint
  name string
}
// 查询时会自动选择 `id`, `name` 字段
db.model(&user{}).limit(10).find(&apiuser{})
// select `id`, `name` from `users` limit 10

注意 queryfields 模式会根据当前 model 的所有字段名称进行 select。

db, err := gorm.open(sqlite.open("gorm.db"), &gorm.config{
  queryfields: true,
})
db.find(&user)
// select `users`.`name`, `users`.`age`, ... from `users` // 带上这个选项
// session mode
db.session(&gorm.session{queryfields: true}).find(&user)
// select `users`.`name`, `users`.`age`, ... from `users`

locking (for update)

gorm 支持多种类型的锁,例如:

db.clauses(clause.locking{strength: "update"}).find(&users)
// select * from `users` for update
db.clauses(clause.locking{
  strength: "share",
  table: clause.table{name: clause.currenttable},
}).find(&users)
// select * from `users` for share of `users`
db.clauses(clause.locking{
  strength: "update",
  options: "nowait",
}).find(&users)
// select * from `users` for update nowait

查看 原生 sql 及构造器 获取详情


子查询

子查询可以嵌套在查询中,gorm 允许在使用 *gorm.db 对象作为参数时生成子查询

db.where("amount > (?)", db.table("orders").select("avg(amount)")).find(&orders)
// select * from "orders" where amount > (select avg(amount) from "orders");
subquery := db.select("avg(age)").where("name like ?", "name%").table("users")
db.select("avg(age) as avgage").group("name").having("avg(age) > (?)", subquery).find(&results)
// select avg(age) as avgage from `users` group by `name` having avg(age) > (select avg(age) from `users` where name like "name%")

from 子查询

gorm 允许我们在 table 方法中通过 from 子句使用子查询,例如:

db.table("(?) as u", db.model(&user{}).select("name", "age")).where("age = ?", 18).find(&user{})
// select * from (select `name`,`age` from `users`) as u where `age` = 18
subquery1 := db.model(&user{}).select("name")
subquery2 := db.model(&pet{}).select("name")
db.table("(?) as u, (?) as p", subquery1, subquery2).find(&user{})
// select * from (select `name` from `users`) as u, (select `name` from `pets`) as p

group 条件

使用 group 条件可以更轻松的编写复杂 sql

db.where(
    db.where("pizza = ?", "pepperoni").where(db.where("size = ?", "small").or("size = ?", "medium")),
).or(
    db.where("pizza = ?", "hawaiian").where("size = ?", "xlarge"),
).find(&pizza{}).statement
// select * from `pizzas` where (pizza = "pepperoni" and (size = "small" or size = "medium")) or (pizza = "hawaiian" and size = "xlarge")

带多个列的 in

带多个列的 in 查询

db.where("(name, age, role) in ?", [][]interface{}{{"jinzhu", 18, "admin"}, {"jinzhu2", 19, "user"}}).find(&users)
// select * from users where (name, age, role) in (("jinzhu", 18, "admin"), ("jinzhu 2", 19, "user"));

命名参数

gorm 支持 sql.namedargmap[string]interface{}{} 形式的命名参数,例如:

db.where("name1 = @name or name2 = @name", sql.named("name", "jinzhu")).find(&user)
// select * from `users` where name1 = "jinzhu" or name2 = "jinzhu"
db.where("name1 = @name or name2 = @name", map[string]interface{}{"name": "jinzhu"}).first(&user)
// select * from `users` where name1 = "jinzhu" or name2 = "jinzhu" order by `users`.`id` limit 1

查看 原生 sql 及构造器 获取详情


find 至 map

gorm 允许扫描结果至 map[string]interface{}[]map[string]interface{},此时别忘了指定 model 或 table,例如:

result := map[string]interface{}{}
db.model(&user{}).first(&result, "id = ?", 1)
var results []map[string]interface{}
db.table("users").find(&results)

firstorinit

获取第一条匹配的记录,或者根据给定的条件初始化一个实例(仅支持 sturct 和 map 条件)

// 未找到 user,则根据给定的条件初始化一条记录
db.firstorinit(&user, user{name: "non_existing"})
// user -> user{name: "non_existing"}
// 找到了 `name` = `jinzhu` 的 user
db.where(user{name: "jinzhu"}).firstorinit(&user)
// user -> user{id: 111, name: "jinzhu", age: 18}
// 找到了 `name` = `jinzhu` 的 user
db.firstorinit(&user, map[string]interface{}{"name": "jinzhu"})
// user -> user{id: 111, name: "jinzhu", age: 18}

如果没有找到记录,可以使用包含更多的属性的结构体初始化 user,attrs 不会被用于生成查询 sql

// 未找到 user,则根据给定的条件以及 attrs 初始化 user
db.where(user{name: "non_existing"}).attrs(user{age: 20}).firstorinit(&user)
// select * from users where name = 'non_existing' order by id limit 1;
// user -> user{name: "non_existing", age: 20}
// 未找到 user,则根据给定的条件以及 attrs 初始化 user
db.where(user{name: "non_existing"}).attrs("age", 20).firstorinit(&user)
// select * from users where name = 'non_existing' order by id limit 1;
// user -> user{name: "non_existing", age: 20}
// 找到了 `name` = `jinzhu` 的 user,则忽略 attrs
db.where(user{name: "jinzhu"}).attrs(user{age: 20}).firstorinit(&user)
// select * from users where name = jinzhu' order by id limit 1;
// user -> user{id: 111, name: "jinzhu", age: 18}

不管是否找到记录,assign 都会将属性赋值给 struct,但这些属性不会被用于生成查询 sql,也不会被保存到数据库

// 未找到 user,根据条件和 assign 属性初始化 struct
db.where(user{name: "non_existing"}).assign(user{age: 20}).firstorinit(&user)
// user -> user{name: "non_existing", age: 20}
// 找到 `name` = `jinzhu` 的记录,依然会更新 assign 相关的属性
db.where(user{name: "jinzhu"}).assign(user{age: 20}).firstorinit(&user)
// select * from users where name = jinzhu' order by id limit 1;
// user -> user{id: 111, name: "jinzhu", age: 20}

firstorcreate

获取匹配的第一条记录或者根据给定条件创建一条新纪录(仅 struct, map 条件有效),rowsaffected 返回创建、更新的记录数

// 未找到 user,根据给定条件创建一条新纪录
result := db.firstorcreate(&user, user{name: "non_existing"})
// insert into "users" (name) values ("non_existing");
// user -> user{id: 112, name: "non_existing"}
// result.rowsaffected // => 1
// 找到 `name` = `jinzhu` 的 user
result := db.where(user{name: "jinzhu"}).firstorcreate(&user)
// user -> user{id: 111, name: "jinzhu", "age": 18}
// result.rowsaffected // => 0

如果没有找到记录,可以使用包含更多的属性的结构体创建记录,attrs 不会被用于生成查询 sql 。

// 未找到 user,根据条件和 assign 属性创建记录
db.where(user{name: "non_existing"}).attrs(user{age: 20}).firstorcreate(&user)
// select * from users where name = 'non_existing' order by id limit 1;
// insert into "users" (name, age) values ("non_existing", 20);
// user -> user{id: 112, name: "non_existing", age: 20}
// 找到了 `name` = `jinzhu` 的 user,则忽略 attrs
db.where(user{name: "jinzhu"}).attrs(user{age: 20}).firstorcreate(&user)
// select * from users where name = 'jinzhu' order by id limit 1;
// user -> user{id: 111, name: "jinzhu", age: 18}

不管是否找到记录,assign 都会将属性赋值给 struct,并将结果写回数据库

// 未找到 user,根据条件和 assign 属性创建记录
db.where(user{name: "non_existing"}).assign(user{age: 20}).firstorcreate(&user)
// select * from users where name = 'non_existing' order by id limit 1;
// insert into "users" (name, age) values ("non_existing", 20);
// user -> user{id: 112, name: "non_existing", age: 20}
// 找到了 `name` = `jinzhu` 的 user,依然会根据 assign 更新记录
db.where(user{name: "jinzhu"}).assign(user{age: 20}).firstorcreate(&user)
// select * from users where name = 'jinzhu' order by id limit 1;
// update users set age=20 where id = 111;
// user -> user{id: 111, name: "jinzhu", age: 20}

优化器、索引提示

优化器提示用于控制查询优化器选择某个查询执行计划,gorm 通过 gorm.io/hints 提供支持,例如:

import "gorm.io/hints"
db.clauses(hints.new("max_execution_time(10000)")).find(&user{})
// select * /*  max_execution_time(10000) */ from `users`

索引提示允许传递索引提示到数据库,以防查询计划器出现混乱。

import "gorm.io/hints"
db.clauses(hints.useindex("idx_user_name")).find(&user{})
// select * from `users` use index (`idx_user_name`)
db.clauses(hints.forceindex("idx_user_name", "idx_user_id").forjoin()).find(&user{})
// select * from `users` force index for join (`idx_user_name`,`idx_user_id`)"

参考 优化器提示、索引、备注 获取详情


迭代

gorm 支持通过行进行迭代

rows, err := db.model(&user{}).where("name = ?", "jinzhu").rows()
defer rows.close()
for rows.next() {
  var user user
  // scanrows 方法用于将一行记录扫描至结构体
  db.scanrows(rows, &user)
  // 业务逻辑...
}

findinbatches

用于批量查询并处理记录

// 每次批量处理 100 条
result := db.where("processed = ?", false).findinbatches(&results, 100, func(tx *gorm.db, batch int) error {
  for _, result := range results {
    // 批量处理找到的记录
  }
  tx.save(&results)
  tx.rowsaffected // 本次批量操作影响的记录数
  batch // batch 1, 2, 3
  // 如果返回错误会终止后续批量操作
  return nil
})
result.error // returned error
result.rowsaffected // 整个批量操作影响的记录数

查询钩子

对于查询操作,gorm 支持 afterfind 钩子,查询记录后会调用它,详情请参考 钩子

func (u *user) afterfind(tx *gorm.db) (err error) {
  if u.role == "" {
    u.role = "user"
  }
  return
}

pluck

pluck 用于从数据库查询单个列,并将结果扫描到切片。如果您想要查询多列,我们应该使用 select 和 scan

var ages []int64
db.model(&users).pluck("age", &ages)
var names []string
db.model(&user{}).pluck("name", &names)
db.table("deleted_users").pluck("name", &names)
// distinct pluck
db.model(&user{}).distinct().pluck("name", &names)
// select distinct `name` from `users`
// 超过一列的查询,应该使用 `scan` 或者 `find`,例如:
db.select("name", "age").scan(&users)
db.select("name", "age").find(&users)

scope

scopes 允许你指定常用的查询,可以在调用方法时引用这些查询

func amountgreaterthan1000(db *gorm.db) *gorm.db {
  return db.where("amount > ?", 1000)
}
func paidwithcreditcard(db *gorm.db) *gorm.db {
  return db.where("pay_mode_sign = ?", "c")
}
func paidwithcod(db *gorm.db) *gorm.db {
  return db.where("pay_mode_sign = ?", "c")
}
func orderstatus(status []string) func (db *gorm.db) *gorm.db {
  return func (db *gorm.db) *gorm.db {
    return db.where("status in (?)", status)
  }
}
db.scopes(amountgreaterthan1000, paidwithcreditcard).find(&orders)
// 查找所有金额大于 1000 的信用卡订单
db.scopes(amountgreaterthan1000, paidwithcod).find(&orders)
// 查找所有金额大于 1000 的货到付款订单
db.scopes(amountgreaterthan1000, orderstatus([]string{"paid", "shipped"})).find(&orders)
// 查找所有金额大于 1000 且已付款或已发货的订单

查看 scopes 获取详情


count

count 用于获取匹配的记录数

var count int64
db.model(&user{}).where("name = ?", "jinzhu").or("name = ?", "jinzhu 2").count(&count)
// select count(1) from users where name = 'jinzhu' or name = 'jinzhu 2'
db.model(&user{}).where("name = ?", "jinzhu").count(&count)
// select count(1) from users where name = 'jinzhu'; (count)
db.table("deleted_users").count(&count)
// select count(1) from deleted_users;
// count with distinct
db.model(&user{}).distinct("name").count(&count)
// select count(distinct(`name`)) from `users`
db.table("deleted_users").select("count(distinct(name))").count(&count)
// select count(distinct(name)) from deleted_users
// count with group
users := []user{
  {name: "name1"},
  {name: "name2"},
  {name: "name3"},
  {name: "name3"},
}
db.model(&user{}).group("name").count(&count)
count // => 3

查看笔记

扫码一下
查看教程更方便
网站地图