gorm day3

gorm day3

  • 查询记录

查询记录

查询单个对象(也就是查一条记录)

GORM提供了First、Take、Last方法,以便从数据库中检索单个对象,当查询数据库时,它添加了LIMIT 1条件,且没有找到记录时,它会返回ErrRecordNotFound错误。
看例子:

// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;

// 获取一条记录,没有指定排序字段(不是基于主键排序)
db.Take(&user)
// SELECT * FROM users LIMIT 1;

// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

result := db.First(&user)
result.RowsAffected // 返回找到的记录数
result.Error        // returns error or nil

// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)

这些功能都说的很详细.
这些方法都有相同的返回值result和err
如果查找的结果啥也没有,那就会返回ErrRecordNotFound这样的错误。
对于这样的错误可以进行错误处理,也可以采用另一种方式来进行避免

如果想避免ErrRecordNotFound错误,可以使用Find,比如db.Limit(1).Find(&user),Find方法可以接收struct和slice的数据。

First和Last会根据主键排序,分别查询第一条和最后一条记录。只有在目标struct是指针或者通过db.Model()指定Model时,该方法才有效,此外,如果相关model没有定义主键,那么将按model的第一个字段进行排序,比如:

var user User
var users []User  

// 有效,因为目标 struct 是指针
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

// 有效,因为通过 `db.Model()` 指定了 model
result := map[string]interface{}{}
db.Model(&User{}).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

// 无效
result := map[string]interface{}{}
db.Table("users").First(&result)

// 配合 Take 有效
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

我总结这个代码:
就是在教你如何正确的使用这些方法:
1.First和Last基本行为: 查询数据库中符合条件的第一条和最后一条记录。前者会根据模型主键进行升序的确定第一条记录,后者进行降序确定最后一条记录。
2.使用条件:
(1)目标 struct 是指针:当直接传递一个模型的指针给 First 或 Last 时,GORM 能够识别模型并根据该模型的主键进行排序。(这种其实就是我在刚学的时候了解到的那种自动推导的功能)
(2)通过 db.Model() 指定模型:如果操作的目标是一个非模型结构体(如 map[string]interface{}),则必须通过 db.Model(&Model{}) 明确指定模型,GORM 会使用这个模型的主键进行排序。
3.无效的情况: 如果直接对 map[string]interface{} 使用 First 而没有通过 db.Model() 明确指定模型,该操作是无效的。因为 First 需要知道主键信息来排序,而 map 类型本身不携带模型信息。
4.配合 Take 使用: Take 方法不依赖主键排序,只返回符合条件的一条记录。因此,即使是对 map[string]interface{} 类型直接使用 db.Table(“users”).Take(&result) 也是有效的,因为它不需要主键信息。
5.未定义主键的情况:
如果相关模型没有定义主键,GORM 将按照模型的第一个字段进行排序。这是 GORM 的一种后备机制,确保 First 和 Last 方法能够在没有明确主键的情况下工作。

重点:
1、db.First(&user),这里传的是要存储的结构体的地址,然后gorm自动根据user的结构推断出要查询的表。
2.db.Model(&User{}),这个就是明确告诉GORM要操作的模型是User,&User{}是一个新的User类型的实例的指针,它用于指定查询的表。.First(&result) 部分执行查询操作,将查询结果填充到 result 中。
这种时候,result可以是一个与User模型结构不同的类型,比如map[string]interface{},这在你想要自定义查询结果的结构时非常有用。
.First(&result) 执行查询,将查询到的第一条记录的数据按照字段名填充到 result 这个 map 中。

这里看这个例子:

type User struct {
    ID    uint
    Name  string
    Email string
}
假设数据库为这样
ID | Name   | Email
---------------------------
1  | Alice  | alice@example.com
2  | Bob    | bob@example.com

//查询ID为1的用户,并将结果存储到一个map[string]interface{}类型的变量中。
package main

import (
    "fmt"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

func main() {
    // 初始化数据库连接(这里以 SQLite 为例)
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }

    // 查询结果将被填充的 map
    result := map[string]interface{}{}

    // 执行查询
    db.Model(&User{}).Where("id = ?", 1).First(&result)

    // 打印结果
    fmt.Println(result)
}

结果:
map[Email:alice@example.com ID:1 Name:Alice]

直接就对应键值对存储好了。


这种方式通常用于动态构造结果或者当你不想创建一个完整的模型结构体来接收查询结果时,这提供了很大的灵活性,但同时也牺牲了类型安全和一些编译时的错误检测。

还有未指定主键,会根据第一个字段排序(即:Code)
到底是什么原理?
这个我简单的说就是它对应的sql语句长这样:SELECT * FROM languages ORDER BY languages.code ASC LIMIT 1

用主键检索

如果主键是数字类型,您可以使用内联条件来检索对象,传入字符串参数时,需要特别注意SQL注入问题,查看安全获取详情。

db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;

db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;

db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);

如果主键是字符串(例如像uuid),查询将被写成这样:

db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";

这其实就是前面的知识,就是个对应查询条件而已。

检索全部对象

// 获取全部记录
result := db.Find(&users)
// SELECT * FROM users;

result.RowsAffected // 返回找到的记录数,相当于 `len(users)`
result.Error        // returns error

这里我细说db.Find()
db.Find(&user) 是 GORM 中用于查询多条记录的方法。这个方法会将查询结果映射填充到传入的参数中,参数通常是一个模型的切片指针。 这样做可以根据定义的模型结构自动构建 SQL 语句,执行查询,并将查询结果填充到所提供的切片中。

示例:
假设你有一个 User 模型定义如下:

type User struct {
    ID   uint
    Name string
}

并且想要查询 users 表中的所有记录,你可以这样使用 db.Find(&users):

var users []User
result := db.Find(&users)

var users []User: 定义一个 User 类型的切片 users,用于存储查询结果。
result := db.Find(&users): 调用 db.Find(&users) 执行查询操作。GORM 会根据 User 模型自动生成 SQL 查询语句,查询 users 表中的所有记录,并将查询结果填充到 users 切片中。
result 是一个 *gorm.DB 类型的对象,包含了操作的结果,例如错误信息、影响的行数等。你可以使用 result.Error 检查查询是否成功,使用 result.RowsAffected 获取查询到的记录数等。

使用场景:
当你需要查询表中的多条记录时,无论是全部记录还是符合特定条件的记录,Find 方法都非常适用。它是 GORM 提供的一个高级抽象,使得数据库查询操作更加简单和直观。

条件

String条件

// 获取第一条匹配的记录
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;

// 获取全部匹配的记录
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';

// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');

// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';

// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;

// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';

// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

这几个例子都是sql语句里面很常见的例子,看看怎么写的,自己写出来其实不难。要注意的就是一个习惯:结构体中字段一般大写开头的,而这里都是全小写,这个就是蛇形复数。

Struct&Map条件

// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;

// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;

// 主键切片条件
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22);

这个例子的话注意每种写法的特点
1、struct要注意Name这里,就是直接实例化一个结构体所具有要查询的字段,然后进行查询。所以这里字段和结构体是保持一致的。
2、这个相比struct的特点就是方便,可以不用构建结构体,想查什么就查什么。
3、查询条件直接是切片,切片的意思对应SQL语句就是in。
注意:当使用结构体作为条件查询时,GORM只会查询非0字段,这意味着如果你的字段值为0,’ ',false或其他零值,该字段不会被用于构建查询条件,例如:

db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu";

这里Age字段由于搞了个零值,所以在背后构建sql语句时就不会被用于构建查询条件。

如果想要包含零值查询条件,你可以使用map,别用这种结构体条件的写法就可以了,map会包含所有key-value的查询条件,例如:

db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;

关于大小写规则, 这个例子中可以看到"Name": “jinzhu”,但是在前一个例子会看到这样"name": “jinzhu”,所以就产生了一些疑问,到底大小写有区别吗,规范写法是哪种?
回答:
都行,规范是大写的那种。
解读:
对于大写的这种而言,Go种的Name和Age字段默认映射到数据库的name和age列。对于map这种类型,理论上应该使用模型种定义的字段名称(也就是大写的)作为键。
根据GORM的文档和默认行为,当你定义模型时,GORM期望结构体字段遵循Go的命名规范(即首字母大写的驼峰式命名),以确保字段是可以导出的,从而可以在GORM内部访问这些字段。
对于查询条件,尽管GORM在内部处理时会将字段名转换为相应的数据库列名(通常是转换为小写并加下划线),建议在使用.Where方法时,按照Go结构体种定义的字段名称来指定查询条件的键(即使用首字母大写的格式)

所以说第二个例子更加符合GORM的使用习惯,更易于理解和保护。

指定结构体查询字段

当你使用struct进行查询时,你可以通过Where()传入struct来指定查询条件的字段,值,表名,例如:

db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;

db.Where(&User{Name: "jinzhu"}, "Age").Find(&users)
// SELECT * FROM users WHERE age = 0;

内联条件

查询条件也可以被内联到First和Find之类的方法中,其用法类似于where。
这句话的意思就是,你不必显示的使用.Where方法来指定查询条件,相反,你可以直接将查询条件作为参数传递给.First、.Find等方法,这样的查询条件将被内联处理,其效果类似于使用.Where方法。这里提供了一种更简洁的语法来执行条件查询。

看这个例子就可以体会到什么时内联:
假设有一个 User 结构体,如下所示:

type User struct {
    ID   uint
    Name string
    Age  int
}

使用 .Where 显式指定查询条件:

var user User
db.Where("name = ?", "jinzhu").First(&user)

这将生成并执行类似于以下的 SQL 语句:

SELECT * FROM `users` WHERE name = 'jinzhu' ORDER BY `users`.`id` LIMIT 1;

使用内联条件:
直接将查询条件内联到 .First 或 .Find 方法中:

var user User
db.First(&user, "name = ?", "jinzhu")

这个内联就是把条件写到这个First条件里面。
或者对于 .Find 方法:

var users []User
db.Find(&users, "age > ?", 18)

这些调用方式将生成和执行与使用 .Where 方法相同效果的 SQL 语句。内联条件的方式使得代码更加简洁,特别是当查询条件简单且直接关联到查询方法时。

Not条件

构建NOT条件,用法与Where类似
这个就是直接搞了个代表sql语句里面的not条件的函数

db.Not("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;

// Not In
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");

// Struct
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;

// 不在主键切片中的记录
db.Not([]int64{1,2,3}).First(&user)
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;

OR条件

这个也是把sql语句里面的or直接整了个函数。

db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';

// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);

// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);

选定特定字段

Select允许您指定从数据库中检索那些字段,在默认情况下GORM会检索所有字段。

db.Select("name", "age").Find(&users)
// SELECT name, age FROM users;

db.Select([]string{"name", "age"}).Find(&users)
// SELECT name, age FROM users;

db.Table("users").Select("COALESCE(age,?)", 42).Rows()
// SELECT COALESCE(age,'42') FROM users;

还是非常的直观的。

ORDER

指定从数据库检索记录时的排序方式。

db.Order("age desc, name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;

// 多个 order
db.Order("age desc").Order("name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;

db.Clauses(clause.OrderBy{
 Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})
// SELECT * FROM users ORDER BY FIELD(id,1,2,3)

解读:
1…Order直接指定了排序规则,即先按age降序排列,如果age相同,则按name升序排列。
2.这个和1没区别,这里就是说明了可以这么用。先按age降序排列,然后再按name升序排列。
3.这个示例展示了如何使用更复杂的自定义排序表达式。通过clause.OrderBy结构体和clause.Expr可以构建自定义的SQL排序表达式,这在GORM默认的排序方法不能满足需求时非常有用。
这里使用了 MySQL 的 FIELD 函数来指定排序顺序。FIELD(id,1,2,3) 意味着先按 id 在给定数组 [1, 2, 3] 中的顺序排列,即先显示 id 为 1 的记录,然后是 id 为 2 的记录,以此类推。
WithoutParentheses: true 表示在构建 SQL 语句时,不会在 FIELD 函数周围添加额外的括号。

LIMIT&Offset

Limit指定获取记录的最大数量,Offset指定在开始返回记录之前要跳过的记录数量。

db.Limit(3).Find(&users)
// SELECT * FROM users LIMIT 3;

// 通过 -1 消除 Limit 条件
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)

db.Offset(3).Find(&users)
// SELECT * FROM users OFFSET 3;
//查询然后跳过前三条记录。

db.Limit(10).Offset(5).Find(&users)
// SELECT * FROM users OFFSET 5 LIMIT 10;

// 通过 -1 消除 Offset 条件
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
// SELECT * FROM users OFFSET 10; (users1)
// SELECT * FROM users; (users2)
//这代表后面那个OFFSET(-1)把前面的offset10这个条件干掉了。

GROUP BY & HAVING

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"

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
for rows.Next() {
 ...
}

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
for rows.Next() {
 ...
}

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)

在 GORM 中使用 Group By 和 Having 子句可以让你对查询结果进行分组并对这些分组应用条件过滤。这在处理聚合查询(如计算总和、平均值等)时非常有用。

使用 Group By
Group By 子句用于根据一个或多个列将返回的记录集合分组。在每个分组上,你可以应用聚合函数(如 SUM(), AVG(), MAX(), MIN() 等)。

使用Having
Having 子句允许你对 Group By 产生的分组结果应用条件过滤,这在你需要基于聚合函数的结果过滤记录时特别有用。

直接看上面例子
例子1:

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)

这个查询在 User 表上操作,选择 name 和 age 的总和(命名为 total)。
它首先使用 Where 子句过滤那些 name 字段以 “group” 开头的记录。
接着,使用 Group(“name”) 对结果按 name 进行分组。
最后,First(&result) 会获取满足条件的第一个分组的结果,并将其映射到 result 结构体实例中。
生成的 SQL 大致为:SELECT name, sum(age) as total FROM users WHERE name LIKE “group%” GROUP BY name LIMIT 1;。

例子2:

db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)

这个查询与前一个类似,但它移除了 Where 子句,并引入了 Having 来过滤分组。
Having(“name = ?”, “group”) 在分组后过滤那些 name 等于 “group” 的分组。
生成的 SQL 大致为:SELECT name, sum(age) as total FROM users GROUP BY name HAVING name = “group”;。

例子3
展示了Rows方法中使用GroupBy和Having,使用rows迭代结果。

Rows()是什么?
在 GORM 中,Rows() 方法用于执行查询并返回一个 *sql.Rows 结果集,它允许你通过遍历来手动处理每一行的数据。这种方法提供了更多的灵活性,因为你可以逐行读取查询结果,并根据需要对每行进行操作。
如何使用Rows()?
使用 Rows() 方法通常涉及以下步骤:
1.执行查询:首先,你会设置好查询条件,然后调用 .Rows() 方法来执行查询。
2.遍历结果集:随后,你可以使用 for rows.Next() 循环来遍历结果集。
3.扫描每行数据:在循环体内,你使用 rows.Scan(&var1, &var2, …) 方法来将当前行的列数据分别扫描到指定的变量中。
4.关闭结果集:最后,不要忘记调用 rows.Close() 来关闭结果集,释放数据库连接资源。
先看个rows的基本案例:

type User struct {
    ID   uint
    Name string
    Age  int
}

// 执行查询
rows, err := db.Model(&User{}).Rows()
if err != nil {
    // 处理错误
}

defer rows.Close()

for rows.Next() {
    var user User
    // 扫描每一行的数据到 user 对象
    err := rows.Scan(&user.ID, &user.Name, &user.Age)
    if err != nil {
        // 处理扫描错误
    }
    // 处理或输出 user 对象
    fmt.Println(user)
}


看完上面这个基本案例,我觉得看这个例子一点都不难。

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
for rows.Next() {
 ...
}

示例4
在Rows中应用Having

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()

与前一个例子类似,但它通过Having子句过滤了那些金额总额大于100的分组。

示例5
使用Scan映射结果到结构体

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)

可以直接这么将查询结果映射到Result类型的切片中。

Distinct

从模型中选择不相同的值

db.Distinct("name", "age").Order("name, age desc").Find(&results)

在 GORM 中,db.Distinct(“name”, “age”) 方法用于选择唯一不同的 name 和 age 值组合的记录,避免在结果集中出现重复项。
我直接举个例子:
原数据库:

ID | Name     | Age
--------------------
1  | Alice    | 30
2  | Bob      | 25
3  | Alice    | 30
4  | Charlie  | 20
5  | Bob      | 28

执行这个语句后

var results []User
db.Distinct("name", "age").Order("name, age desc").Find(&results)

查询结果:

Name     | Age
---------------
Alice    | 30
Bob      | 28
Bob      | 25
Charlie  | 20

会发现重复的Alice和30那一条数据做了去重。所以Distinct核心就是去重。

Joins预加载

你可以使用Joins实现单条Sql预加载管理记录,例如

db.Joins("Company").Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;

条件连接:

db.Joins("Company").Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;

解读:
1.啥是连接?
直接通过这个场景理解:有张表,我这个表里面只有一些字段,但是如果我连接了另一张表之后,就相当于我这个表多了一些字段,但是这些字段都放在另一张表中。
2.连接表的目的
扩展数据:通过连接操作,你可以从其他表中获取额外的信息,扩展查询结果中的数据。
关联查询:连接允许你根据两个表之间的关系进行查询,如获取所有用户及其对应的订单信息。
3.连接表后的字段
当你对表进行连接后,结果集中将包含来自所有参与连接的表的字段。例如,如果你将用户表(users)和订单表(orders)通过用户ID连接,那么结果集中将包含用户表和订单表的所有字段。
在 GORM 中,你可以通过选择特定的字段来限制结果集中包含的列,或者将查询结果映射到具有特定字段的结构体中。
4.怎么连接
在 GORM 中,Joins 方法用于实现 SQL 中的 JOIN 操作,允许你结合来自不同表的数据进行查询。这对于处理关联数据特别有用,比如获取用户及其订单信息等。下面是 Joins 方法的一些常用用法示例和解释。
基本用法:
假设有两个模型:User 和 Profile,其中 User 拥有一个 Profile,通过 UserID 关联。

type User struct {
    ID      uint
    Name    string
    Profile Profile
}

type Profile struct {
    ID     uint
    UserID uint
    Email  string
}

1. 简单的 JOIN
要获取每个用户及其对应的档案信息,可以这样使用 Joins:

var users []User
db.Joins("Profile").Find(&users)

这将执行一个内部连接(INNER JOIN),将 users 表和 profiles 表连接在一起,基于 Profile 字段的配置(假设 GORM 能够自动推断出 JOIN 条件)。

2. 指定 JOIN 条件
如果 GORM 不能自动推断 JOIN 条件,或者你需要指定特定的 JOIN 逻辑,可以直接写出 JOIN 语句:
var users []User
db.Joins(“inner join profiles on profiles.user_id = users.id”).Find(&users)
这里,你明确地指出了如何连接 users 表和 profiles 表:通过 profiles.user_id 和 users.id 字段。

3. 选择特定的字段:
在 JOIN 查询中,你可能只对某些字段感兴趣:

var result []struct {
    UserName string
    Email    string
}

db.Model(&User{}).Select("users.name as user_name, profiles.email as email").
    Joins("inner join profiles on profiles.user_id = users.id").
    Scan(&result)

这个查询选择了 users 表的 name 字段和 profiles 表的 email 字段,并将结果映射到一个自定义的结构体切片 result 中。

4. 使用参数化 JOIN 条件
如果你需要根据特定条件进行 JOIN 查询,可以使用参数:

var users []User
db.Joins("left join profiles on profiles.user_id = users.id and profiles.active = ?", true).Find(&users)

5.使用 Joins 与 Where 结合
这种就是进一步根据特定条件进行join查询

var users []User
db.Joins("Profile").Where("profiles.email LIKE ?", "%@example.com").Find(&users)

总结:
Joins 是 GORM 中非常强大的功能,它允许你灵活地构建复杂的查询,结合不同表中的数据。通过适当地使用 Joins,你可以在单个查询中获取丰富的关联数据,有效地减少数据库查询的次数和提高应用性能。

现在回去看最开始的例子简简单单。

扫描结果

使用find方法

type Result struct {
 Name string
 Age  int
}

var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)

// Raw SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)

就是把结果存到result里面,注意一下这两个使用情况,虽然用法不同,但是效果相同。

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值