sql - 将 "SELECT *"列读入 []string in go

标签 sql csv go

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

Go 提供了优秀的 sqlcsv api,但 csv 需要字符串数组,Rows 中的 Scan 方法会根据类型“填充”字段。 由于我之前不认识表格,所以我不知道有多少列以及它们的类型是什么。

这是我的第一个 Go 程序,所以我有点吃力。

如何最好地将 Rows 实例中的列读入 []string - 这是“正确”的方式吗?

谢谢!

更新

我还在为这些参数而苦恼。这是我的代码,现在我使用 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
}

为此,我得到 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)
    }

这导致 panic :sql:列索引 0 上的扫描错误:目标不是指针

更新 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
}

最佳答案

为了直接扫描值到[]string,你必须创建一个[]interface{} slice 指向每个字符串 slice 中的字符串。

这里有一个 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)
    }
}

关于sql - 将 "SELECT *"列读入 []string in go,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55611846/

相关文章:

sql - 如何将带时区的日期时间插入 SQLite?

java - 将 SET 语句与普通语句组合

sql - SQL语法错误,语法不正确?

r - fread 未正确读取列名

php - 如何使用 php 替换 CSV 文件中一行中的 1 个值?

performance - 为什么与strconv.Atoi相比,strconv.ParseUint这么慢?

go - 使用其他文件的功能是其他目录Golang

sql - 使用 oracle SQL 按分隔符位置拆分字符串

Python:将 "- hrs - min - sec' 字符串更改为分钟十进制

struct - 在 golang 结构中转换字符串