web-dev-qa-db-ja.com

「SELECT *」列をgoの[] stringに読み込む

_SELECT *_を使用して、データベーステーブルの行をcsvファイルにダンプするGoプログラムを作成します。

Goは優れた sql および csv APIを提供しますが、csvは文字列の配列とScanRowsメソッドを必要としますタイプに従ってフィールドを「埋める」。私は以前にテーブルを知らなかったので、列がいくつあるのか、そしてそれらの型が何なのか分かりません。

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
}
_

このために、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: 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
}
_
34
Arne

値を直接Scan[]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)
    }
}
39
ANisus

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

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

また、csvは文字列のみであるため、[] byte型をScannerのdest型として使用します。 docuによると:

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

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

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

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

5
fmt.Println.MKO

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

var tmpErrors string

_ = row.Scan(&tmpErrors)

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

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

0
farhany

次のコードは要件をきれいに示しています。このコードは https://Gist.github.com/hygull/645c3dc39c69b6b69c06f5ea9deee41f で入手できます。テーブルデータも提供されています。

/**
    {
        "created_on": "26 may 2017",
        "todos": [
            "go get github.com/go-sql-driver/mysql"     
        ],
        "aim": "Reading fname column into []string(slice of strings)"
    }
*/


/* 
mysql> select * from users;
+----+-----------+----------+----------+-------------------------------+--------------+
| id | fname     | lname    | uname    | email                         | contact      |
+----+-----------+----------+----------+-------------------------------+--------------+
|  1 | Rishikesh | Agrawani | hygull   | [email protected] | 917353787704 |
|  2 | Sandeep   | E        | sandeep  | [email protected]       | 919739040038 |
|  3 | Darshan   | Sidar    | darshan  | [email protected]        | 917996917565 |
|  4 | Surendra  | Prajapat | surendra | [email protected]     | 918385894407 |
|  5 | Mukesh    | Jakhar   | mukesh   | [email protected]     | 919772254140 |
+----+-----------+----------+----------+-------------------------------+--------------+
5 rows in set (0.00 sec)

mysql> 
*/

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

func main() {
    // db, err := sql.Open("mysql", "<username>:<password>@tcp(127.0.0.1:<port>)/<dbname>?charset=utf8" )
    db, err := sql.Open("mysql", "hygull:admin@67@tcp(127.0.0.1:3306)/practice_db?charset=utf8")

    if err != nil {
        log.Fatal(err)
    }

    rows, err := db.Query("select fname from users")

    if err != nil {
        log.Fatal(err)
    }

    firstnames:=[]string{}
    for rows.Next() {
        var fname string
        rows.Scan(&fname)
        firstnames = append(firstnames, fname)
    }

    fmt.Println(firstnames)
    db.Close()
}

/* 
[Rishikesh Sandeep Darshan Surendra Mukesh]
*/
0
hygull