检索单个对象

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

  1. // 获取第一条记录(主键升序)
    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

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

If you want to avoid the ErrRecordNotFound error, you could use Find like db.Limit(1).Find(&user)

The First, Last method will find the first/last record order by primary key, it only works when querying with struct or provides model value, if no primary key defined for current model, will order by the first field, for example:

  1. var user User

    // 可以
    db.First(&user)
    // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

    // 可以
    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)

    // 根据第一个字段排序
    type Language struct {
    Code string
    Name string
    }
    db.First(&Language{})
    // SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1

根据主键检索

Objects can be retrieved using primary key by using Inline Conditions. Be extra careful with strings to avoid SQL Injection, check out Security section for details

  1. 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);

检索全部对象

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

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

条件

String 条件

  1. // 获取第一条匹配的记录
    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';

Struct & Map 条件

  1. // 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);

NOTE When querying with struct, GORM will only query with non-zero fields, that means if your field’s value is 0, '', false or other zero values, it won’t be used to build query conditions, for example:

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

You can use map to build the query condition, it will use all values, e.g:

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

Or refer Specify Struct search fields

Specify Struct search fields

When searching with struct, you could use its field name or dbname as arguments to specify the searching fields, for example:

  1. 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;

Inline Condition

Works similar to Where.

  1. // SELECT * FROM users WHERE id = 23;
    // Get by primary key if it were a non-integer type
    db.First(&user, "id = ?", "string_primary_key")
    // SELECT * FROM users WHERE id = 'string_primary_key';

    // Plain SQL
    db.Find(&user, "name = ?", "jinzhu")
    // SELECT * FROM users WHERE name = "jinzhu";

    db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
    // SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;

    // Struct
    db.Find(&users, User{Age: 20})
    // SELECT * FROM users WHERE age = 20;

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

Not Conditions

Build NOT conditions, works similar to Where

  1. 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;

    // Not In slice of primary keys
    db.Not([]int64{1,2,3}).First(&user)
    // SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;

Or Conditions

  1. 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);

Also check out Group Conditions in Advanced Query, it can be used to write complicated SQL

选择特定字段

Specify fields that you want to retrieve from database, by default, select all fields

  1. 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;

Also check out Smart Select Fields

Order

Specify order when retrieving records from the database

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

    // Multiple orders
    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)

Limit & Offset

Limit specify the max number of records to retrieve Offset specify the number of records to skip before starting to return the records

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

    // Cancel limit condition with -1
    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;

    // Cancel offset condition with -1
    db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
    // SELECT * FROM users OFFSET 10; (users1)
    // SELECT * FROM users; (users2)

Checkout Pagination for how to make a paginator

Group & Having

  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)
    // SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name`


    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)

Distinct

Selecting distinct values from the model

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

Distinct works with Pluck, Count also

Joins

Specify Joins conditions

  1. type result struct {
    Name string
    Email string
    }
    db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
    // SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id

    rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
    for rows.Next() {
    ...
    }

    db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)

    // multiple joins with parameter
    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)

Joins Preloading

You can use Joins eager loading associations with a single SQL, for example:

  1. 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`;

Refer Preloading (Eager Loading) for details

Scan

Scan results into a struct work similar to Find

  1. 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)