2013-01-23 24 views
21

Tôi muốn viết chương trình Go để kết xuất các hàng từ bảng cơ sở dữ liệu vào tệp csv bằng cách sử dụng SELECT *.đọc các cột "SELECT *" vào chuỗi [] ở chế độ

Go cung cấp tuyệt vời sqlcsv apis, nhưng csv hy vọng mảng các chuỗi và Scan phương pháp trong Rows "lấp đầy" các lĩnh vực theo từng loại. Vì tôi không biết bảng trước, tôi không biết có bao nhiêu cột và loại của chúng là gì.

Đây là chương trình đầu tiên của tôi ở Go, vì vậy tôi đang phải vật lộn một chút.

Làm cách nào để đọc tốt nhất các cột từ một ví dụ Rows thành một số []string - và đó có phải là cách "đúng" không?

Cảm ơn!

CẬP NHẬT

tôi vẫn phải vật lộn với các thông số. Đây là mã của tôi, hiện tại tôi đang sử dụng panic thay vì trả lại error, nhưng tôi sẽ thay đổi điều đó sau. Trong thử nghiệm của tôi, tôi chuyển kết quả truy vấn và 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 
} 

Đối với điều này, tôi nhận được cannot use cols (type []string) as type []interface {} in function argument (tại dòng writer.Write(cols).

sau đó tôi thử nghiệm

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

dẫn đến panic: sql: Scan error on column index 0: destination not a pointer.

UPDATE 2

Tôi đã độc lập đến giải pháp ANisus '. Đây là mã tôi đang sử dụng bây giờ.

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 
} 

Trả lời

23

Để trực tiếp Scan các giá trị vào []string, bạn phải tạo một slice []interface{} trỏ đến từng chuỗi trong chuỗi chuỗi của bạn.

Ở đây bạn có một ví dụ làm việc cho MySQL (chỉ cần thay đổi sql.Open -Command để khớp với cài đặt của bạn):

package main 

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

func main() { 
    db, err := sql.Open("mysql", "user:[email protected](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) 
    } 
} 
+0

Xin chào, tuyệt! Tôi vừa mới đến đó và muốn đăng nó trong một bản chỉnh sửa. Cảm ơn! Đó sẽ là câu trả lời của tôi và tôi sẽ upvote nó, nhưng tôi vẫn có một câu hỏi: Tôi muốn điều này được nhanh chóng và tôi muốn thoát khỏi chuỗi giá trị và chuyển đổi nil để \ N. Làm cách nào để bao gồm các chuyển đổi trong mã của tôi tốt nhất? – Arne

+0

Giải pháp của tôi nằm cách xa nước. Nó yêu cầu các giá trị để có thể được lưu trữ trong một 'chuỗi'. Ví dụ. nếu bạn nhận được giá trị NULL từ db, bạn sẽ gặp lỗi vì chuỗi không thể là 'nil'. Tôi đã cập nhật câu trả lời bằng cách sử dụng byte [] và kiểm tra các giá trị 'nil'. – ANisus

+0

Bạn có thể xem xét sử dụng 'sql.RawBytes' thay vì' [] byte' –

5

để có được Số cột (và cũng là tên) chỉ cần sử dụng Cột() Chức năng

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

và như csv chỉ có thể là một chuỗi, chỉ cần sử dụng một [] byte gõ kiểu dest cho Scanner. theo docu:

If an argument has type *[]byte, Scan saves in that argument a copy of the corresponding data. The copy is owned by the caller and can be modified and held indefinitely.

dữ liệu sẽ không được chuyển thành loại thực của nó. và từ byte [] này, bạn có thể chuyển đổi nó thành chuỗi.

nếu bạn chắc chắn bảng của bạn chỉ sử dụng các loại cơ sở (string [] byte, nil, int (s), float (s), bool), bạn có thể trực tiếp vượt qua chuỗi như dest

nhưng nếu bạn sử dụng các loại khác như mảng, enums, hoặc như vậy, sau đó dữ liệu không thể được chuyển thành chuỗi. nhưng điều này cũng phụ thuộc vào cách trình điều khiển xử lý các loại này.(ví dụ một vài tháng trước, trình điều khiển hậu kỳ không thể xử lý mảng, vì vậy anh ấy luôn trả lại [] byte mà tôi cần để tự biến đổi nó)

+0

Cảm ơn, hiện tại mục tiêu này chỉ là MySQL, vì vậy, đó chỉ là các loại cơ sở. – Arne

+0

Hãy chắc chắn chấp nhận câu trả lời này nếu nó phù hợp với bạn (và nếu không có câu trả lời nào tốt hơn) – weberc2

+0

Tôi sẽ, nhưng tôi vẫn đang vật lộn với sự chuyển đổi từ '[] interface {}' trong Scan thành '[] string' trong Viết. Việc chuyển chuỗi '[] vào trong Quét không biên dịch được - tôi chưa sửa câu trả lời của mình. Tôi đã upvote, mặc dù. – Arne

0

Các mã sau đây cho đẹp thêm statisfies yêu cầu của bạn, bạn có thể nhận được mã này ở https://gist.github.com/hygull/645c3dc39c69b6b69c06f5ea9deee41f. Dữ liệu bảng cũng đã được cung cấp.

/** 
    { 
     "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:[email protected]@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] 
*/ 
Các vấn đề liên quan