1. 查询

1.1. 查询

  1. // 获取第一条记录,按主键排序
  2. db.First(&user)
  3. //// SELECT * FROM users ORDER BY id LIMIT 1;
  4. // 获取一条记录,不指定排序
  5. db.Take(&user)
  6. //// SELECT * FROM users LIMIT 1;
  7. // 获取最后一条记录,按主键排序
  8. db.Last(&user)
  9. //// SELECT * FROM users ORDER BY id DESC LIMIT 1;
  10. // 获取所有的记录
  11. db.Find(&users)
  12. //// SELECT * FROM users;
  13. // 通过主键进行查询 (仅适用于主键是数字类型)
  14. db.First(&user, 10)
  15. //// SELECT * FROM users WHERE id = 10;

1.1.1. Where

原生 SQL

  1. // 获取第一条匹配的记录
  2. db.Where("name = ?", "jinzhu").First(&user)
  3. //// SELECT * FROM users WHERE name = 'jinzhu' limit 1;
  4. // 获取所有匹配的记录
  5. db.Where("name = ?", "jinzhu").Find(&users)
  6. //// SELECT * FROM users WHERE name = 'jinzhu';
  7. // <>
  8. db.Where("name <> ?", "jinzhu").Find(&users)
  9. // IN
  10. db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
  11. // LIKE
  12. db.Where("name LIKE ?", "%jin%").Find(&users)
  13. // AND
  14. db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
  15. // Time
  16. db.Where("updated_at > ?", lastWeek).Find(&users)
  17. // BETWEEN
  18. db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)

Struct & Map

  1. // Struct
  2. db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
  3. //// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;
  4. // Map
  5. db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
  6. //// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
  7. // 多主键 slice 查询
  8. db.Where([]int64{20, 21, 22}).Find(&users)
  9. //// SELECT * FROM users WHERE id IN (20, 21, 22);

NOTE 当通过struct进行查询的时候,GORM 将会查询这些字段的非零值, 意味着你的字段包含 0''false 或者其他 零值, 将不会出现在查询语句中, 例如:

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

你可以考虑适用指针类型或者 scanner/valuer 来避免这种情况。

  1. // 使用指针类型
  2. type User struct {
  3. gorm.Model
  4. Name string
  5. Age *int
  6. }
  7. // 使用 scanner/valuer
  8. type User struct {
  9. gorm.Model
  10. Name string
  11. Age sql.NullInt64
  12. }

1.1.2. Not

Where查询类似

  1. db.Not("name", "jinzhu").First(&user)
  2. //// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;
  3. // 不包含
  4. db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users)
  5. //// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
  6. //不在主键 slice 中
  7. db.Not([]int64{1,2,3}).First(&user)
  8. //// SELECT * FROM users WHERE id NOT IN (1,2,3);
  9. db.Not([]int64{}).First(&user)
  10. //// SELECT * FROM users;
  11. // 原生 SQL
  12. db.Not("name = ?", "jinzhu").First(&user)
  13. //// SELECT * FROM users WHERE NOT(name = "jinzhu");
  14. // Struct
  15. db.Not(User{Name: "jinzhu"}).First(&user)
  16. //// SELECT * FROM users WHERE name <> "jinzhu";

1.1.3. Or

  1. db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
  2. //// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
  3. // Struct
  4. db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
  5. //// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';
  6. // Map
  7. db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users)
  8. //// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';

1.1.4. 行内条件查询

Where 查询类似。

需要注意的是,当使用链式调用传入行内条件查询时,这些查询不会被传参给后续的中间方法。

  1. // 通过主键进行查询 (仅适用于主键是数字类型)
  2. db.First(&user, 23)
  3. //// SELECT * FROM users WHERE id = 23 LIMIT 1;
  4. // 非数字类型的主键查询
  5. db.First(&user, "id = ?", "string_primary_key")
  6. //// SELECT * FROM users WHERE id = 'string_primary_key' LIMIT 1;
  7. // 原生 SQL
  8. db.Find(&user, "name = ?", "jinzhu")
  9. //// SELECT * FROM users WHERE name = "jinzhu";
  10. db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
  11. //// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
  12. // Struct
  13. db.Find(&users, User{Age: 20})
  14. //// SELECT * FROM users WHERE age = 20;
  15. // Map
  16. db.Find(&users, map[string]interface{}{"age": 20})
  17. //// SELECT * FROM users WHERE age = 20;

1.1.5. 额外的查询选项

  1. // 为查询 SQL 添加额外的选项
  2. db.Set("gorm:query_option", "FOR UPDATE").First(&user, 10)
  3. //// SELECT * FROM users WHERE id = 10 FOR UPDATE;

1.2. FirstOrInit

获取第一条匹配的记录,或者通过给定的条件下初始一条新的记录(仅适用与于 struct 和 map 条件)。

  1. // 未查询到
  2. db.FirstOrInit(&user, User{Name: "non_existing"})
  3. //// user -> User{Name: "non_existing"}
  4. // 查询到
  5. db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
  6. //// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
  7. db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
  8. //// user -> User{Id: 111, Name: "Jinzhu", Age: 20}

1.2.1. Attrs

如果未找到记录,则使用参数初始化 struct

  1. // 未查询到
  2. db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
  3. //// SELECT * FROM USERS WHERE name = 'non_existing';
  4. //// user -> User{Name: "non_existing", Age: 20}
  5. db.Where(User{Name: "non_existing"}).Attrs("age", 20).FirstOrInit(&user)
  6. //// SELECT * FROM USERS WHERE name = 'non_existing';
  7. //// user -> User{Name: "non_existing", Age: 20}
  8. // 查询到
  9. db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
  10. //// SELECT * FROM USERS WHERE name = jinzhu';
  11. //// user -> User{Id: 111, Name: "Jinzhu", Age: 20}

1.2.2. Assign

无论是否查询到数据,都将参数赋值给 struct

  1. // 未查询到
  2. db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
  3. //// user -> User{Name: "non_existing", Age: 20}
  4. // 查询到
  5. db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrInit(&user)
  6. //// SELECT * FROM USERS WHERE name = jinzhu';
  7. //// user -> User{Id: 111, Name: "Jinzhu", Age: 30}

1.3. FirstOrCreate

获取第一条匹配的记录,或者通过给定的条件创建一条记录 (仅适用与于 struct 和 map 条件)。

  1. // 未查询到
  2. db.FirstOrCreate(&user, User{Name: "non_existing"})
  3. //// INSERT INTO "users" (name) VALUES ("non_existing");
  4. //// user -> User{Id: 112, Name: "non_existing"}
  5. // 查询到
  6. db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user)
  7. //// user -> User{Id: 111, Name: "Jinzhu"}

1.3.1. Attrs

如果未查询到记录,通过给定的参数赋值给 struct ,然后使用这些值添加一条记录。

  1. // 未查询到
  2. db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
  3. //// SELECT * FROM users WHERE name = 'non_existing';
  4. //// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
  5. //// user -> User{Id: 112, Name: "non_existing", Age: 20}
  6. // 查询到
  7. db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
  8. //// SELECT * FROM users WHERE name = 'jinzhu';
  9. //// user -> User{Id: 111, Name: "jinzhu", Age: 20}

1.3.2. Assign

无论是否查询到,都将其分配给记录,并保存到数据库中。

  1. // 未查询到
  2. db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
  3. //// SELECT * FROM users WHERE name = 'non_existing';
  4. //// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
  5. //// user -> User{Id: 112, Name: "non_existing", Age: 20}
  6. // 查询到
  7. db.Where(User{Name: "jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user)
  8. //// SELECT * FROM users WHERE name = 'jinzhu';
  9. //// UPDATE users SET age=30 WHERE id = 111;
  10. //// user -> User{Id: 111, Name: "jinzhu", Age: 30}

1.4. 高级查询

1.4.1. 子查询

使用 *gorm.expr 进行子查询

  1. db.Where("amount > ?", DB.Table("orders").Select("AVG(amount)").Where("state = ?", "paid").QueryExpr()).Find(&orders)
  2. // SELECT * FROM "orders" WHERE "orders"."deleted_at" IS NULL AND (amount > (SELECT AVG(amount) FROM "orders" WHERE (state = 'paid')));

1.4.2. 查询

指定要从数据库检索的字段,默认情况下,将选择所有字段。

  1. db.Select("name, age").Find(&users)
  2. //// SELECT name, age FROM users;
  3. db.Select([]string{"name", "age"}).Find(&users)
  4. //// SELECT name, age FROM users;
  5. db.Table("users").Select("COALESCE(age,?)", 42).Rows()
  6. //// SELECT COALESCE(age,'42') FROM users;

1.4.3. Order

使用 Order 从数据库查询记录时,当第二个参数设置为 true 时,将会覆盖之前的定义条件。

  1. db.Order("age desc, name").Find(&users)
  2. //// SELECT * FROM users ORDER BY age desc, name;
  3. // 多个排序条件
  4. db.Order("age desc").Order("name").Find(&users)
  5. //// SELECT * FROM users ORDER BY age desc, name;
  6. // 重新排序
  7. db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
  8. //// SELECT * FROM users ORDER BY age desc; (users1)
  9. //// SELECT * FROM users ORDER BY age; (users2)

1.4.4. Limit

指定要查询的最大记录数

  1. db.Limit(3).Find(&users)
  2. //// SELECT * FROM users LIMIT 3;
  3. // 用 -1 取消 LIMIT 限制条件
  4. db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
  5. //// SELECT * FROM users LIMIT 10; (users1)
  6. //// SELECT * FROM users; (users2)

1.4.5. Offset

指定在开始返回记录之前要跳过的记录数。

  1. db.Offset(3).Find(&users)
  2. //// SELECT * FROM users OFFSET 3;
  3. // 用 -1 取消 OFFSET 限制条件
  4. db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
  5. //// SELECT * FROM users OFFSET 10; (users1)
  6. //// SELECT * FROM users; (users2)

1.4.6. Count

获取模型记录数

  1. db.Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Find(&users).Count(&count)
  2. //// SELECT * from USERS WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (users)
  3. //// SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)
  4. db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
  5. //// SELECT count(*) FROM users WHERE name = 'jinzhu'; (count)
  6. db.Table("deleted_users").Count(&count)
  7. //// SELECT count(*) FROM deleted_users;

注意: 在查询链中使用 Count 时,必须放在最后一个位置,因为它会覆盖 SELECT 查询条件。

1.4.7. Group 和 Having

  1. rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
  2. for rows.Next() {
  3. ...
  4. }
  5. rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
  6. for rows.Next() {
  7. ...
  8. }
  9. type Result struct {
  10. Date time.Time
  11. Total int64
  12. }
  13. db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)

1.4.8. Joins

指定关联条件

  1. rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
  2. for rows.Next() {
  3. ...
  4. }
  5. db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
  6. // 多个关联查询
  7. db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)

1.5. Pluck

使用 Pluck 从模型中查询单个列作为集合。如果想查询多个列,应该使用 Scan 代替。

  1. var ages []int64
  2. db.Find(&users).Pluck("age", &ages)
  3. var names []string
  4. db.Model(&User{}).Pluck("name", &names)
  5. db.Table("deleted_users").Pluck("name", &names)
  6. // Requesting more than one column? Do it like this:
  7. db.Select("name, age").Find(&users)

1.6. Scan

将 Scan 查询结果放入另一个结构体中。

  1. type Result struct {
  2. Name string
  3. Age int
  4. }
  5. var result Result
  6. db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)
  7. // Raw SQL
  8. db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)