PAYATHON 2026

Go 使用 goqu 查询 SQLite BLOB(16) UUID 为什么没有结果?

数据阿明

结论

SQLite 会按字节内容比较 BLOB。插入和查询时如果绑定的是同一组 16 字节,WHERE id = ? 就应能匹配。十六进制显示相同还不能证明查询参数与表中的 BLOB 完全一致,还需要检查生成的 SQL、参数类型和字节内容,以及查询连接所用的数据库文件和记录。

插入代码将 uuid.New().MarshalBinary() 的结果写入 id,查询代码则把 id.MarshalBinary() 的结果放进 goqu.Ex。需要确认两次 MarshalBinary() 使用的是同一个 UUID,且传给 SQLite 的值都是 []byte。

先检查 goqu 生成的 SQL 和参数

将数据集设为 prepared 模式,并打印 SQL、参数类型和字节内容:

sqlString, args, err := db.From("master").
    Select("id", "username").
    Where(goqu.Ex{"id": idBin}).
    Prepared(true).
    ToSQL()
if err != nil {
    return err
}

fmt.Printf("SQL: %s\n", sqlString)
fmt.Printf("args: %#v\n", args)
for i, arg := range args {
    fmt.Printf("arg[%d]: type=%T value=%x\n", i, arg, arg)
}

检查 SQL 是否包含占位符,参数列表是否包含查询用的 UUID 字节,以及参数类型是否为 []byte。如果 args 为空,或 SQL 已经内嵌了值,就不能认为 QueryRow(args...) 执行的是带 UUID 参数的查询。不同 goqu 方言生成的 SQL 可能不同,应以打印结果为准。

对比数据库中的实际 BLOB

先查看 SQLite 中保存的类型和字节:

SELECT typeof(id), length(id), hex(id)
FROM master;

typeof(id) 应为 blob,length(id) 应为 16。将目标记录的 hex(id) 与 Go 中 idBin 的 %x 输出逐字节比较。如果表中有多条记录,可以按预期的 UUID 十六进制值筛选,确认检查的是目标记录。

用 database/sql 做对照查询

暂时绕开 goqu,直接传入字节切片查询:

var idBytes []byte
var username string

err := conn.QueryRow(
    `SELECT id, username FROM master WHERE id = ?`,
    idBin,
).Scan(&idBytes, &username)
if err != nil {
    return err
}

这里的 conn 应是实际连接到目标数据库的 *sql.DB 或 *sql.Tx。如果直接查询能找到记录、goqu 查询却找不到,检查 goqu 的方言、生成的 SQL 和 args。如果直接查询也找不到,则检查插入和查询是否使用了同一个 UUID 字节,以及连接是否指向同一个数据库文件。

注意事项

uuid.New() 每次都会生成新的 UUID。如果插入和查询时分别调用 uuid.New(),两组字节就会不同。应保存插入时的 UUID,并用同一个值查询。SQLite 的 BLOB(16) 声明不会自动把值转换成 UUID,也不会按 UUID 的文本形式比较。问题描述没有提供实际生成的 SQL、驱动名称,也没有说明插入和查询使用的 UUID 是否相同,因此仅凭现有片段无法确定未匹配的原因。

备注:内容仅供参考。