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

bkzy-wangjp · 2018-12-23 01:20:38 · 2401 次点击

谢谢楼上@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
更多评论

Scan error on column index 0: destination not a pointer

是要找人中译英么?

传入的不是指针。

你需要穿个指针进去。

#1

正巧我前几天研究这个问题。 我的解法不知道很好,不过解决了我的问题。

如果有专业人士,也请大家给出更完美的方案。

主要就是用了类型查询,得到内部实际类型,然后自己去构造指针放到Scan前的slice里面, 再调用 rows.Scan(arr ... ) 去获取实际数据, 最终将内部的数据再通过反射取出来。

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