MySQL 查询操作

QueryDB

根据条件查询的三种方式,当条件无效时会返回 ErrRecordNotFound 错误

  • First
    • SELECT * FROM users ORDER BY id LIMIT 1;
  • Take
    • SELECT * FROM users LIMIT 1;
  • Last
    • SELECT * FROM users ORDER BY id DESC LIMIT 1;

Find 查询全表并返回条件结果,查询过程不会报错 ErrRecordNotFound db.Limit(1).Find(&user)

First/Last

First/Last 根据主键排序(// no primary key defined, results will be ordered by first field),返回第一条排序数据,将数组传递到指针对象

  • db.First(&user)
  • db.Model(&User{}).First(&result)

根据主键查询

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

根据条件查询

// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;

// Get all matched records
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';

Query within struct/mapping

// 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;

// Slice of primary keys
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22);

struct 查询中只会查询非空值,不能采用默认值进行查询

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

map 查询支持各种值,包含默认值查询

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

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;

// 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

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

Order

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;

Limit

limit: 限量查询总数 offset: 查询起点

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)

原生 SQL:派生列与函数(COALESCE / NULLIF / 计算列)

Gorm 帮我们拼 SQL,但有些查询用原生 SQL 更直接,尤其是需要从已有列「算」出新字段时。例如从 llm_providers 表列出所有提供商,并同时算出「展示名」和「是否配了密钥」:

SELECT
    name,
    COALESCE(NULLIF(display_name, ''), name) AS display_name,
    enabled,
    (api_key IS NOT NULL AND api_key != '') AS has_key
FROM llm_providers
ORDER BY name;

各列含义:

  • name:提供商标识(原表列)
  • COALESCE(NULLIF(display_name, ''), name) AS display_name:展示名。内层 NULLIF(display_name, '') 把空串转成 NULL,外层 COALESCE(..., name) 取第一个非 NULL 值——display_name 为空时回退用 name
  • enabled:是否启用(原表列)
  • (api_key IS NOT NULL AND api_key != '') AS has_key:布尔计算列,密钥非空且非空串时为 true,否则 false

在 Go 里用 database/sql 读取(派生列要对应到普通变量,类型按数据库返回决定):

rows, err := db.Query(`
    SELECT name,
           COALESCE(NULLIF(display_name, ''), name),
           enabled,
           (api_key IS NOT NULL AND api_key != '') AS has_key
    FROM llm_providers ORDER BY name`)
if err != nil {
	log.Printf("query failed: %v", err)
	return
}
defer rows.Close()

for rows.Next() {
	var name, displayName string
	var enabled, hasKey bool
	if err := rows.Scan(&name, &displayName, &enabled, &hasKey); err != nil {
		log.Printf("scan failed: %v", err)
		continue
	}
	log.Printf("provider=%s display=%s enabled=%v hasKey=%v", name, displayName, enabled, hasKey)
}
if err := rows.Err(); err != nil { // 遍历结束必须检查读取过程中的错误
	log.Printf("rows error: %v", err)
}

要点:

  • 计算列(如 has_key)在 Go 端就是一个普通 bool(数据库侧类型通常是 boolean 或 tinyint)
  • 遍历完务必调用 rows.Err() 检查,且别忘了 defer rows.Close()
  • 占位符差异:MySQL 用 ?,PostgreSQL 用 $1/$2;本例无参数,故不涉及。通用机制见 数据库与 SQL