本文详解如何通过 database/sql 接口配合 mattn/go-sqlite3 驱动,安全、高效地执行 COUNT() 聚合查询并提取整型结果值,避免常见类型错误与 SQL 注入风险。
本文详解如何通过 `database/sql` 接口配合 `mattn/go-sqlite3` 驱动,安全、高效地执行 `count()` 聚合查询并提取整型结果值,避免常见类型错误与 sql 注入风险。
在 Go 中使用 SQLite 执行计数查询(如 SELECT COUNT(...) FROM ...)时,不能直接将结果赋给 string 类型变量——因为 COUNT() 返回的是整数(INTEGER),SQLite 驱动会将其映射为 Go 的 int64。若错误声明为 string,Scan() 将 panic 报错:sql: Scan error on column index 0: unsupported Scan, storing driver.Value type int64 into type *string。
正确的做法是使用 QueryRow() 配合占位符参数化查询,并声明匹配的 Go 整型变量(推荐 int64 或 int)。以下是推荐的完整实现:
id := "1234"
// ✅ 使用参数化查询(防止 SQL 注入)
query := "SELECT COUNT(notebook) FROM pages WHERE notebook = ?"
var count int64
err := db.QueryRow(query, id).Scan(&count)
if err != nil {
switch {
case err == sql.ErrNoRows:
fmt.Println("未找到匹配的 notebook 记录")
default:
log.Printf("查询出错: %v", err)
}
return
}
fmt.Printf("共找到 %d 条 notebook=%s 的记录\n", count, id)关键要点说明:
- ✅ 必须使用 ? 占位符 + 可变参数传参,禁止字符串拼接(如 fmt.Sprint(query, id)),否则存在严重 SQL 注入漏洞;
- ✅ COUNT() 总是返回一行一列,因此优先使用 QueryRow() 而非 Query()(后者需手动调用 Next() 和 Scan(),且易遗漏 Close());
- ✅ 声明接收变量为 int64(SQLite 的 COUNT 默认映射为 int64),若确定数据量小可转为 int:countInt := int(count);
- ⚠️ defer query.Close() 仅适用于 Prepare() 后的语句对象,而上述写法使用 QueryRow() 的快捷路径,无需显式 prepare/close,更简洁安全。
进阶提示:
若需同时查询多个聚合值(如 COUNT(*), MAX(created_at)),可扩展 Scan() 参数:
var total int64
var latest time.Time
err := db.QueryRow("SELECT COUNT(*), MAX(ctime) FROM pages WHERE notebook = ?", id).
Scan(&total, &latest)总之,牢记三原则:参数化、类型匹配、错误判空——即可稳健获取 COUNT 结果。


















