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

bkzy-wangjp · 2018-12-23 01:20:38 · 2390 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2018-12-23 01:20:38 的主题,其中的信息可能已经有所发展或是发生改变。

读取数据库的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] 有没有大神处理过此类问题?


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2390 次点击  
加入收藏 微博
6 回复  |  直到 2018-12-26 09:37:27
jarlyyn
jarlyyn · #1 · 6年之前

Scan error on column index 0: destination not a pointer

是要找人中译英么?

传入的不是指针。

你需要穿个指针进去。

moryaden
moryaden · #2 · 6年之前

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

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

主要就是用了类型查询,得到内部实际类型,然后自己去构造指针放到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()
}
bkzy-wangjp
bkzy-wangjp · #3 · 6年之前

谢谢楼上@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)
    }
}
weiwg521
weiwg521 · #4 · 6年之前

这个在go-SQL-driver/mysql的官网有示例啊,认真看一看,接口切片存的是string或其它类型的地址。

https://github.com/go-sql-driver/mysql/wiki/Examples

bkzy-wangjp
bkzy-wangjp · #5 · 6年之前
weiwg521weiwg521 #4 回复

这个在go-SQL-driver/mysql的官网有示例啊,认真看一看,接口切片存的是string或其它类型的地址。 >https://github.com/go-sql-driver/mysql/wiki/Examples

谢谢,问题已解决。

victorl
victorl · #6 · 6年之前

可以用一下轻度封装的sqlx

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传