Golang读取MySQL数据库,数据列动态变化的处理

bkzy-wangjp · · 2241 次点击
正巧我前几天研究这个问题。 我的解法不知道很好,不过解决了我的问题。 如果有专业人士,也请大家给出更完美的方案。 主要就是用了类型查询,得到内部实际类型,然后自己去构造指针放到`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