go-sql-driver/mysql绑定变量查询时返回结果类型不一致问题
问题根因
这个现象是go-sql-driver/mysql在不同查询模式下使用不同MySQL通信协议导致的类型返回差异:
- 硬编码LIMIT参数执行SQL时,驱动走文本查询协议:服务端返回的所有列值统一以字节流形式传输,因此INT类型的
log_id也会被解析为[]byte类型,存储对应数字的ASCII字节值,原有类型断言和转换逻辑可以正常运行。 - 使用
?占位符传参执行SQL时,驱动默认走预处理语句(Prepared Statement)二进制协议:协议会严格按照列定义的类型返回对应Go类型值,INT类型列会直接返回int64类型的数值,不再是[]byte,因此直接做[]byte类型断言会失败,拿到nil切片。
VARCHAR类型的user字段在两种协议下都会返回[]byte类型,因此两种场景下都能正常解析。
解决方案
二选一即可:
方案1:兼容两种返回类型(推荐)
修改扫描后的类型处理逻辑,不要硬编码断言为[]byte,根据实际返回类型做分支处理,示例代码:
for i, colname := range col { rawValue := *(values[i].(*interface{})) var val string var numVal int64 switch v := rawValue.(type) { case []byte: val = string(v) numVal, _ = strconv.ParseInt(val, 10, 64) case int64: numVal = v val = strconv.FormatInt(v, 10) case nil: // 按需处理NULL值场景 m[colname] = nil continue } if colname == "log_id" || colname == "log_msg_id" { m[colname] = int(numVal) } else { m[colname] = val } }
这种写法可以同时兼容文本协议、二进制协议的返回值,也能正确处理NULL值场景,稳定性更高。
方案2:强制驱动走文本协议
如果不想修改现有解析逻辑,可以在MySQL连接DSN中添加interpolateParams=true参数,驱动会在客户端本地完成参数拼接,直接走文本协议执行查询,和硬编码参数的行为完全一致,修改后的DSN示例:
db, _:= sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/log?interpolateParams=true")
该参数在v1.6.0版本的go-sql-driver/mysql中已经支持,可以直接使用。
内容的提问来源于stack exchange,提问作者hatemath
相关产品推荐
相关产品推荐

