gorm中的基本查询
检索单个对象
GORM 提供了 First
、Take
、Last
方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1
条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 获取第一条记录(主键升序) db.First(&user) // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1 // 获取第一条记录,没有指定排序字段 db.Take(&user) // SELECT * FROM `users` LIMIT 1 // 获取最后一条记录,主键降序 db.Last(&user) // SELECT * FROM `users` ORDER BY `users`.`id` DESC LIMIT 1 result := db.Last(&user) fmt.Println(result.RowsAffected) // 返回找到的记录数 fmt.Println(result.Error) // return error or nil b := errors.Is(result.Error, gorm.ErrRecordNotFound) // 判断错误类型,如果是此错误类型,返回true,否则返回false fmt.Println(b) 如果你想避免ErrRecordNotFound错误,你可以使用Find,比如db.Limit(1).Find(&user),Find方法可以接受 struct 和slice的数据。 |
First
和 Last
会根据主键排序,分别查询第一条和最后一条记录。 只有在目标 struct 是指针或者通过 db.Model()
指定 model 时,该方法才有效。 此外,如果相关 model 没有定义主键,那么将按 model 的第一个字段进行排序。
1 2 3 4 5 6 7 8 | // 有效,因为目标是结构体指针 db.First(&user) // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1 // 有效,因为通过db.Model() 指定了model var result map [string] interface {} db.Model(new(User)).First(&result) // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1// 无效var result map[string]interface{}db.Table("users").First(&result)fmt.Println(result) // 输出:map[]// 配合Take无效var result map[string]interface{}db.Table("users").Take(&result)// 未指定主键,会根据第一个字段排序(即:`Code`)type Language struct { Code string Name string}db.First(&Language{})// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1 |
用主键检错
如果主键是数字类型,您可以使用内联条件来检索对象,传入字符串参数时,需要特别注意 SQL 注入问题
1 2 | db.First(&user, 2) // SELECT * FROM `users` WHERE `users`.`id` = 2 ORDER BY `users`.`id` LIMIT 1db.First(&user, "2")<br>// SELECT * FROM `users` WHERE `users`.`id` = 2 ORDER BY `users`.`id` LIMIT 1db.Find(&users, []int{1, 2})<br>// SELECT * FROM `users` WHERE `users`.`id` IN (1,2) |
如果主键是字符串(例如像 uuid),查询将被写成这样:
db.First(&user, "uuid = ?", "00000000-0000-0000-0000-000000000000")
// SELECT * FROM `users` WHERE uuid = '00000000-0000-0000-0000-000000000000' ORDER BY `users`.`id` LIMIT 1
检索全部对象
result := db.Find(&users)
// SELECT * FROM `users`
myPrint(result.RowsAffected) // 返回的记录行数,相当于 len(users)
myPrint(result.Error) // return error
String 条件
1 | // 获取第一条匹配的记录<br>db.Where("name = ?", "老王").First(&user)<br>// SELECT * FROM `users` WHERE name = '老王' ORDER BY `users`.`id` LIMIT 1<br><br>// 获取全部匹配的记录<br>db.Where("name = ?", "老王").Find(&users)<br>// SELECT * FROM `users` WHERE name = '老王'<br><br>// IN<br>db.Debug().Where("age IN (?)", []uint8{10, 55}).Find(&users)<br>// SELECT * FROM `users` WHERE age IN (10,55)<br><br>// LIKE<br>db.Debug().Where("age LIKE ?", "%0%").Find(&users)<br>// SELECT * FROM `users` WHERE age LIKE '%0%'<br><br>// AND<br>db.Debug().Where("age=? AND name=?", 20, "老王").Find(&users)<br><em>// SELECT * FROM `users` WHERE age=20 AND name='老王'<br><br>// TIME<br></em>db.Debug().Where("created_at < ?", time.Now()).Find(&users)<br>// SELECT * FROM `users` WHERE created_at < '2021-11-17 13:34:51.519'<em>// BETWEEN<br></em>db.Debug().Where("age BETWEEN ? AND ?", 10, 19).Find(&users)<br>// SELECT * FROM `users` WHERE age BETWEEN 10 AND 19 |
Struct & Map 条件
1 | // struct<br><br>var users []User<br>db.Debug().Where(&User{Name: sql.NullString{"老王", true}, Age: 20}).Find(&users)<br><em><em><em>// SELECT * FROM `users` WHERE `users`.`name` = '老王' AND `users`.`age` = 20<br><br>// map<br></em></em></em>db.Debug().Where(map[string]interface{}{<br> "name": sql.NullString{"李四", true}, "age": 10,<br>}).First(&user)<br>// SELECT * FROM `users` WHERE `age` = 10 AND `name` = '李四' ORDER BY `users`.`id` LIMIT 1// 主键切片条件<br>var users []User<br>db.Debug().Where([]uint{1, 5}).Find(&users)<br>// SELECT * FROM `users` WHERE `users`.`id` IN (1,5) |
注意:当使用结构体作为查询条件时,GORM只会查询非零值的字段,这意味着如果您的字段为0、false、""、或其它零值时,该字段不会被用于构建查询条件,例如:
db.Debug().Where(&User{Name: sql.NullString{"老王", true}, Age: 0}).Find(&users)
// SELECT * FROM `users` WHERE `users`.`name` = '老王'
如果你想包含零值的查询条件,你可以使用map,其会包含所有key-value的查询条件
db.Debug().Where(map[string]interface{}{
"name": sql.NullString{"老王", true}, "age": 0,
}).Find(&users)
// SELECT * FROM `users` WHERE `age` = 0 AND `name` = '老王'
指定结构体查询字段
当使用struct进行查询时,你可以通过想where()传入struct来指定查询条件的字段、值、表名,例如
db.Debug().Where(&User{Name: sql.NullString{"老王", true}}, "name", "Age").Find(&users)
// SELECT * FROM `users` WHERE `users`.`name` = '老王' AND `users`.`age` = 0
db.Debug().Where(&User{Name: sql.NullString{"老王", true}, Age: 20}, "age").Find(&users)
// SELECT * FROM `users` WHERE `users`.`age` = 20
也就是说where里面放的结构体无论有几个字段程序不管,最终只会按照结构体后面的参数字段进行条件查询
内联条件
查询条件也可以被内联到First或Find之类的方法中,其用法类似于Where
1 | // 根据主键获取记录,如果是非整型主键<br>db.Debug().First(&user, "ID = ?", 5)<br>// SELECT * FROM `users` WHERE ID = 5 ORDER BY `users`.`id` LIMIT 1<br><br><em><em><em>// plain sql<br></em></em></em>db.Debug().Find(&users, "name = ?", "老王")<br>// SELECT * FROM `users` WHERE name = '老王'db.Debug().Find(&users, "name <> ? AND age = ?", "老王", 20)<br>// SELECT * FROM `users` WHERE name <> '老王' AND age = 20<br><br><br>// structdb.Debug().Find(&users, &User{Name: sql.NullString{"赵六", true}})<br>// SELECT * FROM `users` WHERE `users`.`name` = '赵六'<br><br><br>// mapdb.Debug().Find(&users, map[string]interface{}{<br> "age": 20,<br>})<br>// SELECT * FROM `users` WHERE `age` = 20 |
Not 条件
构建not条件,用法与Where类型
db.Debug().Not("name <> ?", "赵六").First(&user)
// SELECT * FROM `users` WHERE NOT name <> '赵六' ORDER BY `users`.`id` LIMIT 1
// Not IN
db.Debug().Not("name IN (?)", []string{"老王", "李四"}).First(&user)
// SELECT * FROM `users` WHERE NOT name IN ('老王','李四') ORDER BY `users`.`id` LIMIT 1
// map
db.Debug().Not(map[string]interface{}{
"name": []string{"老王", "李四"},
}).First(&user)
// SELECT * FROM `users` WHERE `name` NOT IN ('老王','李四') ORDER BY `users`.`id` LIMIT 1
// struct
db.Debug().Not(User{Age: uint8(10), Name: sql.NullString{"老王", true}}).Find(&users)
// SELECT * FROM `users` WHERE (`users`.`name` <> '老王' AND `users`.`age` <> 10)
// 不在主键切片中的记录
db.Debug().Not([]uint{1, 2, 3, 5}).Find(&users)
// SELECT * FROM `users` WHERE `users`.`id` NOT IN (1,2,3,5)
Or 条件
db.Debug().Where("name = ?", "赵六").Or("age = ?", 10).Find(&users)
// SELECT * FROM `users` WHERE name = '赵六' OR age = 10
// struct
db.Debug().Where("name = ?", "赵六").Or(&User{Age: 55}).Find(&users)
// SELECT * FROM `users` WHERE name = '赵六' OR `users`.`age` = 55
// map
db.Debug().Where("name = ?", "赵六").Or(map[string]interface{}{
"age": 55,
}).Find(&users)
// SELECT * FROM `users` WHERE name = '赵六' OR `age` = 55
选择特定字段
Select
允许您指定从数据库中检索哪些字段, 默认情况下,GORM 会检索所有字段。
1 2 | db.Debug().Select( "name" , "age" ).Find(&users) // SELECT `name`,`age` FROM `users`<br><br>db.Debug().Select([]string{"name", "age"}).Find(&users)<br>// SELECT `name`,`age` FROM `users` |
Order
指定从数据库检索记录时的排序方式
db.Debug().Order("age asc, name desc").Find(&users)
// SELECT * FROM `users` ORDER BY age asc, name desc
// 多个order
db.Debug().Order("age desc").Order("name").Find(&users)
// SELECT * FROM `users` ORDER BY age desc,name
Limit & Offset
limit
指定获取记录的最大数量 Offset
指定在开始返回记录之前要跳过的记录数量
1 2 | db.Debug().Limit(3).Find(&users) // SELECT * FROM `users` LIMIT 3<br><br><br>// 通过-1消除limit条件db.Debug().Limit(1).Find(&users1).Limit(-1).Find(&users2)<br>// SELECT * FROM `users` LIMIT 1<br>// SELECT * FROM `users`<em id="__mceDel"><br><em id="__mceDel">db.Debug().Limit(1).Offset(3).Find(&users)<br></em></em><em><em>// SELECT * FROM `users` LIMIT 1 OFFSET 3<br><br><br>// 通过-1消除Offset条件<br></em></em>db.Debug().Offset(3).Offset(-1).Find(&users)<br>// SELECT * FROM `users` |
注意:Offset要和Limit一起使用,单独使用会报错:Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '3' at line 1
Group By & Having
1 2 3 4 5 6 7 8 9 10 | type Result struct { Date time.Time Total int } db.Model(&User{}).Select( "name, sum(age) as total" ).Where( "name LIKE ?" , "group%" ).Group( "name" ).First(&result) // SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name` LIMIT 1 db.Model(&User{}).Select( "name, sum(age) as total" ).Group( "name" ).Having( "name = ?" , "group" ).Find(&result) // SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"// 智能选择字段<br>type APIUser struct {<br> Name string<br> Total int<br>}<br>rows, err := db.Debug().Table("users").Select("name, sum(age) as total").Group("name").Having("total > ?", 11).Rows()<br>// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING total > 11<br>for rows.Next() {<br> var users APIUser<br> db.ScanRows(rows, &users)<br> myPrint(users)<br>}<br>/*<br>type: main.APIUser value: {老王 65}<br>type: main.APIUser value: {马亚南 12}<br>*/type Result struct { Date time.Time Total int64 } db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results) |
Distinct
1 2 | db.Debug().Distinct( "date" , "total" ).Order( "total desc, date desc" ).Find(&results) // SELECT DISTINCT `date`,`total` FROM `results` ORDER BY total desc, date desc |
Joins
1 2 | db.Debug().Model(new(User)).Select( "users.name, emails.email" ).Joins( "left join emails on users.id = emails.user_id" ).Scan(&results) // SELECT users.name, emails.email FROM `users` left join emails on users.id = emails.user_id<br><br>rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()<br>for rows.Next() {<br> fmt.Println(rows)<br>}<br><br>db.Debug().Table("users").Select("users.name, emails.email").Joins("left join emails on users.id = emails.user_id").Scan(&results)<br>// SELECT users.name, emails.email FROM `users` left join emails on users.id = emails.user_id<br><br><br>// <em>带参数的多表连接<br></em>// 方法一<br>//db.Debug().Joins("JOIN users ON users.id=emails.user_id AND users.name=?", "老王").Find(&emails)<br>// SELECT `emails`.`user_id`,`emails`.`email` FROM `emails` JOIN users ON users.id=emails.user_id AND users.name='老王'<br>// 方法二<br>db.Debug().Joins("JOIN users ON users.id=emails.user_id").Where("users.name=?", "李四").Find(&emails)<br>// SELECT `emails`.`user_id`,`emails`.`email` FROM `emails` JOIN users ON users.id=emails.user_id WHERE users.name='李四' |
Joins 预加载
您可以使用 Joins
实现单条 SQL 预加载关联记录,例如:
Scan
scan将结果放到结构体中的方式与Find类似
1 2 | db.Debug().Table( "users" ).Joins( "left join emails on users.id=emails.user_id" ).Scan(&results) // SELECT * FROM `users` left join emails on users.id=emails.user_id<br><br><br>// 原生SQLdb.Debug().Raw("select name, email from users inner join emails on users.id=emails.user_id where users.name=?", "李四").Scan(&results)<br>// select name, email from users inner join emails on users.id=emails.user_id where users.name='李四' |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)