我想编写一个Go程序,以使用将数据从数据库表中的行转储到csv文件中SELECT *。
SELECT *
Go提供了出色的sql和csv api,但是csv希望根据它们的类型Scan在Rows“填充”字段中使用字符串数组和方法。由于我以前不知道该表,所以我不知道有多少列以及它们的类型是什么。
csv
Scan
Rows
这是我在Go中的第一个程序,所以我有点挣扎。
如何最好地将Rows实例中的列读入[]string-,这是“正确”的方式吗?
[]string
谢谢!
更新
我仍然在参数上苦苦挣扎。这是我的代码,目前我使用的panic不是返回error,而是稍后再进行更改。在测试中,我传递了查询结果和os.Stdout。
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)在线。
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。
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 }
为了Scan将值直接转换成[]string,您必须创建一个[]interface{}指向字符串切片中每个字符串的切片。
[]interface{}
在这里,您有一个适用的MySQL示例(只需更改sql.Open-command以匹配您的设置):
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) } }