读取数据库的SELECT语句是动态变化的,因此查询出来的列也是变化的。按照网上查找的资料,使用下面的方法进行处理:
cols, err := rows.Columns() // Remember to check err afterwards
vals := make([]interface{}, len(cols))
for i, _ := range cols {
vals[i] = new(sql.RawBytes)
}
for rows.Next() {
err = rows.Scan(vals...)
// Now you can check each element of vals for nil-ness,
// and you can use type introspection and type assertions
// to fetch the column into a typed variable.
}
但是err = rows.Scan(vals...)这一句总是执行不了,报错:sql: Scan error on column index 0: destination not a pointer
我用的具体程序是:
for rows.Next() {
err := rows.Scan(vals...)
checkErr(err)
for i := range vals {
mt.Print(vals[i], " ")
}
fmt.Print("\n")
}
但是使用下面的却可以执行:
for rows.Next() {
err := rows.Scan(&vals[0], &vals[1], &vals[2], &vals[3])
checkErr(err)
for i := range vals {
mt.Print(vals[i], " ")
}
fmt.Print("\n")
}
但是打印出来的结果是ASCII码,如下:
[53 57 51 53 52 57 57 55 54] [50 48 49 56 45 49 50 45 50 50 32 49 54 58 51 57 58 48 50] [54 54 48 89 76 46 80 86] [48 46 48 53 50 53 57 50 49 57 57 50 57 53 55 53 57 50]
[53 57 51 53 53 48 49 54 57] [50 48 49 56 45 49 50 45 50 50 32 49 54 58 51 57 58 48 52] [54 54 48 89 76 46 80 86] [48 46 48 53 51 51 50 53 53 52 49 51 49 55 52 54 50 57 50]
[53 57 51 53 53 48 51 51 51] [50 48 49 56 45 49 50 45 50 50 32 49 54 58 51 57 58 48 54] [54 54 48 89 76 46 80 86] [48 46 48 53 51 56 56 52 53 56 52 52 53 54 54 56 50 50 48 53]
有没有大神处理过此类问题?
正巧我前几天研究这个问题。
我的解法不知道很好,不过解决了我的问题。
如果有专业人士,也请大家给出更完美的方案。
主要就是用了类型查询,得到内部实际类型,然后自己去构造指针放到`Scan`前的slice里面,
再调用 `rows.Scan(arr ... )` 去获取实际数据,
最终将内部的数据再通过反射取出来。
```go
package main
import (
"bytes"
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
/*
mysql> desc users;
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(40) | NO | | NULL | |
| password | varchar(256) | NO | | NULL | |
| create_time | datetime | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)
mysql> select * from users;
+----+----------+----------+---------------------+
| id | username | password | create_time |
+----+----------+----------+---------------------+
| 1 | aa | aa | 2018-12-01 00:00:00 |
| 2 | bb | bb | 2018-12-02 00:00:00 |
| 3 | cc | cc | 2018-12-03 00:00:00 |
| 4 | dd | dd | 2018-12-04 00:00:00 |
+----+----------+----------+---------------------+
4 rows in set (0.00 sec)
*/
func withReflect() {
db, err := sql.Open("mysql", "go:go@/go")
if err != nil {
fmt.Println("open db failed")
return
}
rows, err := db.Query("select * from users")
if err != nil {
fmt.Println("query db failed")
return
}
defer rows.Close()
colTypes, err := rows.ColumnTypes()
if err != nil {
fmt.Println("coltypes failed")
return
}
var data = make(map[uint32]interface{})
for rows.Next() {
var id uint32
var built = make([]interface{}, 0)
built = append([]interface{}{&id})
for _, ct := range colTypes[1:] {
switch ct.DatabaseTypeName() {
case "VARCHAR":
fallthrough
case "DATETIME":
built = append(built, new(string))
default:
var i interface{}
built = append(built, &i)
fmt.Printf("type name is %v\n", ct.DatabaseTypeName())
}
}
err = rows.Scan(built...)
if err != nil {
fmt.Println(err)
continue
}
data[id] = built[1:]
}
for k, v := range data {
ss := &bytes.Buffer{}
vlist := v.([]interface{})
for _, tt := range vlist {
switch realStr := tt.(type) {
case *string:
fmt.Fprintf(ss, "%v, ", *realStr)
default:
fmt.Fprintf(ss, "uknown, ")
}
}
fmt.Printf("k = %v, v = %v\n", k, ss.String())
}
return
}
func main() {
withReflect()
}
```
#2
更多评论
Scan error on column index 0: destination not a pointer
是要找人中译英么?
传入的不是指针。
你需要穿个指针进去。
#1
谢谢楼上@jarlyyn和@moryaden的建议和意见。我的问题通过自己的方法已经解决了,我的方法比楼上的稍微简单一些,就是先建立一个`vals := make([]interface{}, len(cols))`,然后再建立一个一样的`valsp := make([]interface{}, len(cols))`,但是名称不一样,然后把第一个变量的各个地址放到第二个数组里面,具体见代码:
cols, err := rows.Columns() //探测读取到的数据的列数
checkErr(err)
vals := make([]interface{}, len(cols)) //建立接口
valsp := make([]interface{}, len(vals)) //建立接口指针的接口
//将接口转换为指针类型的接口
for i := range vals {
valsp[i] = &vals[i]
}
//解析查询结果
for rows.Next() {
if err := rows.Scan(valsp...); err == nil { //注意:此处用valsp
for i := range vals { //注意:此处用vals
if v, ok := vals[i].([]byte); ok { //注意:读取的数据是uint8类型的数组,需要转成byte类型的数组才好转换成其他
fmt.Print(string(v), " ") //输出各个列
}
}
fmt.Print("\n") //输出换行符
} else {
checkErr(err)
}
}
#3