44

を使用して、データベース テーブルから csv ファイルに行をダンプする Go プログラムを作成したいと考えていますSELECT *

Go は優れたsqlおよびcsv APIを提供しcsvますが、文字列の配列と、型に応じて「塗りつぶし」フィールドScan内のメソッドを想定しています。Rows以前はテーブルを知らなかったので、列がいくつあり、それらの型が何であるかわかりません。

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 argumentwriter.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: 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
}
4

4 に答える 4

52

Scan値をに直接渡すには、文字列スライス内の各文字列を指すスライスを[]string作成する必要があります。[]interface{}

ここに MySQL の実際の例があります (sql.Open設定に合わせて -command を変更するだけです):

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)
    }
}
于 2013-01-24T11:52:50.767 に答える
6

列数 (および名前) を取得するには、Columns() 関数を使用します。

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

csv は文字列にしかできないため、[]byte 型を Scanner の dest 型として使用します。ドキュメントによると:

引数の型が *[]byte の場合、Scan はその引数に対応するデータのコピーを保存します。コピーは呼び出し元が所有し、無期限に変更および保持できます。

データは実際の型に変換されません。この [] バイトから、文字列に変換できます。

テーブルが基本型 (string、[]byte、nil、int(s)、float(s)、bool) のみを使用していることが確実な場合は、string を dest として直接渡すことができます。

ただし、配列や列挙型などの他の型を使用すると、データを文字列に変換できません。ただし、これはドライバーがこの型を処理する方法にも依存します。(例として数か月前、postgresドライバーは配列を処理できなかったため、自分で変換する必要がある場所で常に[]バイトを返しました)

于 2013-01-23T13:02:36.457 に答える
-1

代わりにこれを行うことはできませんでしたか?以下簡略化。

var tmpErrors string

_ = row.Scan(&tmpErrors)

actualVarHere := strings.Split(tmpErrors, "\n")

表示されていない問題やパフォーマンスの問題はありますか?

于 2019-04-29T20:18:08.640 に答える