在go中将"SELECT*"列读入[]字符串

Arn*_*rne 36 sql csv go

我想编写一个Go程序,用于将数据库表中的行转储到csv文件中SELECT *.

Go提供了优秀的sqlcsv apis,但是csv期望字符串数组和"填充"字段中的Scan方法Rows根据它们的类型.由于我之前不知道这张表,我不知道有多少列以及它们的类型.

这是我在Go的第一个项目,所以我有点挣扎.

我如何最好地将Rows实例中的列读入[]string- 并且是"正确"的方式?

谢谢!

UPDATE

我仍然在努力参与这些参数.这是我的代码,现在我正在使用panic而不是返回error,但我稍后会改变它.在我的测试中,我传递了查询结果和os.Stdout.

func dumpTable(rows *sql.Rows, out io.Writer) error {
    colNames, err := rows.Columns()
    if err != nil {
        panic(err)
    }
    if rows.Next() {
        writer := csv.NewWriter(out)
        writer.Comma = '\t'
        cols := make([]string, len(colNames))
        processRow := func() {
            err := rows.Scan(cols...)
            if err != nil {
                panic(err)
            }
            writer.Write(cols)
        }
        processRow()
        for rows.Next() {
            processRow()
        }
        writer.Flush()
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

为此,我得到了cannot use cols (type []string) as type []interface {} in function argument(writer.Write(cols)在线.

然后我测试了

    readCols := make([]interface{}, len(colNames))
    writeCols := make([]string, len(colNames))
    processRow := func() {
        err := rows.Scan(readCols...)
        if err != nil {
            panic(err)
        }
        // ... CONVERSION?
        writer.Write(writeCols)
    }
Run Code Online (Sandbox Code Playgroud)

导致panic: sql: Scan error on column index 0: destination not a pointer.

更新2

我独立地达到了ANisus的解决方案.这是我现在使用的代码.

func dumpTable(rows *sql.Rows, out io.Writer) error {
    colNames, err := rows.Columns()
    if err != nil {
        panic(err)
    }
    writer := csv.NewWriter(out)
    writer.Comma = '\t'
    readCols := make([]interface{}, len(colNames))
    writeCols := make([]string, len(colNames))
    for i, _ := range writeCols {
        readCols[i] = &writeCols[i]
    }
    for rows.Next() {
        err := rows.Scan(readCols...)
        if err != nil {
            panic(err)
        }
        writer.Write(writeCols)
    }
    if err = rows.Err(); err != nil {
        panic(err)
    }
    writer.Flush()
    return nil
}
Run Code Online (Sandbox Code Playgroud)

ANi*_*sus 40

要将Scan值直接输入a []string,必须创建[]interface{}指向字符串切片中每个字符串的切片.

这里有一个MySQL的工作示例(只需更改sql.Open命令以匹配您的设置):

package main

import (
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "database/sql"
)

func main() {
    db, err := sql.Open("mysql", "user:pass@tcp(localhost:3306)/test?charset=utf8")
    defer db.Close()

    if err != nil {
        fmt.Println("Failed to connect", err)
        return
    }

    rows, err := db.Query(`SELECT 'one' col1, 'two' col2, 3 col3, NULL col4`)
    if err != nil {
        fmt.Println("Failed to run query", err)
        return
    }

    cols, err := rows.Columns()
    if err != nil {
        fmt.Println("Failed to get columns", err)
        return
    }

    // Result is your slice string.
    rawResult := make([][]byte, len(cols))
    result := make([]string, len(cols))

    dest := make([]interface{}, len(cols)) // A temporary interface{} slice
    for i, _ := range rawResult {
        dest[i] = &rawResult[i] // Put pointers to each string in the interface slice
    }

    for rows.Next() {
        err = rows.Scan(dest...)
        if err != nil {
            fmt.Println("Failed to scan row", err)
            return
        }

        for i, raw := range rawResult {
            if raw == nil {
                result[i] = "\\N"
            } else {
                result[i] = string(raw)
            }
        }

        fmt.Printf("%#v\n", result)
    }
}
Run Code Online (Sandbox Code Playgroud)


fmt*_*MKO 5

获取列数(以及名称)只需使用Columns()函数

http://golang.org/pkg/database/sql/#Rows.Columns

并且由于csv只能是一个字符串,只需使用[]字节类型作为扫描器的目标类型.据记录:

如果参数的类型为*[] byte,则Scan会在该参数中保存相应数据的副本.该副本由调用者拥有,可以无限期地修改和保留.

数据不会转换为真实的类型.然后从这个[]字节,您可以将其转换为字符串.

如果你确定你的表只使用基类型(字符串,[] byte,nil,int(s),float(s),bool)你可以直接传递字符串作为dest

但如果您使用其他类型,如数组,枚举等,则数据无法转换为字符串.但这也取决于驱动程序如何处理这种类型.(几个月前作为例子,postgres驱动程序无法处理数组,所以他总是返回[]字节,我需要自己转换它)