我想编写一个Go程序,使用SELECT *
将数据库表中的行转储到csv文件中。
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 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
}
答案 0 :(得分:38)
为了将值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)
}
}
答案 1 :(得分:5)
获取列数(以及名称)只需使用Columns()函数
http://golang.org/pkg/database/sql/#Rows.Columns
并且由于csv只能是一个字符串,只需使用[]字节类型作为扫描器的目标类型。 据记录:
如果参数的类型为* [] byte,则Scan会在该参数中保存一份副本 相应的数据。该副本由调用者拥有,可以 修改并无限期地持有。
数据不会转换为真实类型。 然后,您可以从此[]字节将其转换为字符串。
如果您确定您的表只使用基类型(字符串,[] byte,nil,int(s),float(s),bool),您可以直接将字符串作为dest传递
但是如果使用其他类型(如数组,枚举等),则数据无法转换为字符串。但这也取决于驱动程序如何处理这种类型。 (几个月前作为例子,postgres驱动程序无法处理数组,所以他总是返回[]字节,我需要自己转换它)
答案 2 :(得分:0)
不能这样做吗?简化如下。
var tmpErrors string
_ = row.Scan(&tmpErrors)
actualVarHere := strings.Split(tmpErrors, "\n")
会不会出现问题或性能问题?
答案 3 :(得分:-1)
以下代码可以满足您的要求,您可以在https://gist.github.com/hygull/645c3dc39c69b6b69c06f5ea9deee41f获取此代码。还提供了表格数据。
List commands = new ArrayList<String>();
commands.add("mysql -u root -p ********");
commands.add("select * from employee;");
System.out.println(commands);
ProcessBuilder pb = new ProcessBuilder(commands);
Process process = pb.start();