Je veux écrire un programme Go pour vider les lignes d'une table de base de données dans un fichier csv en utilisant SELECT *
.
Go fournit l'excellent sql et csv apis, mais csv
attend des tableaux de chaînes et la méthode Scan
dans Rows
"remplit" les champs selon leur type. Comme je ne connais pas le tableau auparavant, je n'ai aucune idée du nombre de colonnes et de leurs types.
C'est mon premier programme à Go, donc je me bats un peu.
Comment puis-je lire au mieux les colonnes d'une instance Rows
dans un []string
- et est-ce la "bonne" façon?
Merci!
[~ # ~] mise à jour [~ # ~]
Je lutte toujours avec les paramètres. Ceci est mon code, pour l'instant j'utilise panic
au lieu de retourner un error
, mais je vais changer cela plus tard. Dans mon test, je passe le résultat de la requête et 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
}
Pour cela, j'obtiens cannot use cols (type []string) as type []interface {} in function argument
(à la ligne writer.Write(cols)
.
J'ai ensuite testé
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)
}
ce qui conduit à panic: sql: Scan error on column index 0: destination not a pointer
.
MISE À JOUR 2
Je suis arrivé indépendamment à ANisus 'solution. C'est le code que j'utilise maintenant.
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
}
Pour directement Scan
les valeurs dans un []string
, vous devez créer un []interface{}
tranche pointant vers chaque chaîne de votre tranche de chaîne.
Voici un exemple de travail pour MySQL (changez simplement le sql.Open
- commande correspondant à vos paramètres):
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)
}
}
pour obtenir le nombre de colonnes (ainsi que les noms), utilisez simplement la fonction Columns ()
http://golang.org/pkg/database/sql/#Rows.Columns
et comme csv ne peut être qu'une chaîne, utilisez simplement un type d'octet [] comme type dest pour le scanner. selon docu:
Si un argument a le type * [] octet, Scan enregistre dans cet argument une copie des données correspondantes. La copie appartient à l'appelant et peut être modifiée et conservée indéfiniment.
les données ne seront pas transformées en leur type réel. et à partir de cet [] octet, vous pouvez ensuite le convertir en chaîne.
si vous êtes sûr que vos tables utilisent uniquement des types de base (chaîne, [] octet, nil, int (s), flottant (s), bool), vous pouvez directement passer la chaîne en tant que dest
mais si vous utilisez d'autres types comme des tableaux, des énumérations, etc., les données ne peuvent pas être transformées en chaîne. mais cela dépend également de la façon dont le pilote gère ces types. (il y a quelques mois à titre d'exemple, le pilote postgres n'était pas capable de gérer les tableaux, donc il retournait toujours [] octet là où j'avais besoin de le transformer par moi-même)
Ne pourrait-on pas faire cela à la place? Simplifié ci-dessous.
var tmpErrors string
_ = row.Scan(&tmpErrors)
actualVarHere := strings.Split(tmpErrors, "\n")
Y aurait-il un problème ou un problème de performances que je ne vois pas?
Le code suivant satisfait joliment votre condition, vous pouvez obtenir ce code à https://Gist.github.com/hygull/645c3dc39c69b6b69c06f5ea9deee41f . Les données du tableau ont également été fournies.
/**
{
"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]
*/