在 Go 的 database/sql 包中,db.QueryRow() 返回的是 *sql.Row 类型,而非 error;必须调用 .Scan() 才会触发实际查询并返回错误(如 sql.ErrNoRows),直接将 QueryRow 赋值给 err 变量会导致类型不匹配编译错误。
在 go 的 database/sql 包中,`db.queryrow()` 返回的是 `*sql.row` 类型,而非 `error`;必须调用 `.scan()` 才会触发实际查询并返回错误(如 `sql.errnorows`),直接将 `queryrow` 赋值给 `err` 变量会导致类型不匹配编译错误。
db.QueryRow() 本身不执行查询,也不返回错误——它只是构建一个惰性查询句柄(*sql.Row)。真正的数据库交互和错误发生时机是在调用 .Scan() 方法时:此时驱动才会执行 SQL、获取结果行,并将数据解码到目标变量中。若查询无匹配记录,Scan() 才会返回 sql.ErrNoRows;若发生其他问题(如连接中断、类型不匹配),则返回对应错误。
因此,以下写法是错误的(类型不兼容,无法编译):
// ❌ 错误:QueryRow() 返回 *sql.Row,不能赋值给 err (error 类型)
err := db.QueryRow("SELECT id, name FROM accounts WHERE steamid = ?", steamid)
// 编译错误:cannot use ... as type error in assignment正确做法是:先调用 QueryRow(),再链式调用 Scan(),并将 Scan() 的返回值(error)用于判断:
var id int
var name string
// ✅ 正确:Scan() 执行查询并返回 error
err := db.QueryRow("SELECT id, name FROM accounts WHERE steamid = ?", steamid).Scan(&id, &name)
switch {
case err == sql.ErrNoRows:
log.Println("未找到该 steamid 对应的账号")
case err != nil:
log.Printf("数据库查询出错: %v", err)
default:
// 查询成功,id 和 name 已被正确填充
fmt.Printf("查到账号: ID=%d, Name=%s\n", id, name)
}⚠️ 注意事项:
- Scan() 的参数必须是指针,且数量、类型需与 SELECT 子句中的列严格匹配(例如 SELECT id, name → Scan(&id, &name));
- 若只关心是否存在记录而无需读取字段,可使用占位符 &struct{}{} 或更推荐的方式:用 QueryRow().Scan() 配合 _ 忽略字段(但依然要提供正确数量的指针);
- sql.ErrNoRows 是唯一可安全用 == 比较的预定义错误,其他数据库错误应使用 errors.Is(err, ...) 判断(Go 1.13+);
- 切勿忽略 Scan() 的返回值——即使 QueryRow() 成功,Scan() 仍可能因空值(NULL)、类型转换失败等报错。
总结:Go 的 SQL 查询错误处理遵循“延迟求值”原则——QueryRow 是准备,Scan 是执行。牢记 “有 Scan,才有 Err”,即可避免此类类型混淆问题。










