web-dev-qa-db-fra.com

Créer une carte à Golang à partir des lignes de la base de données

Fondamentalement, après avoir fait une requête, j'aimerais prendre les lignes résultantes et produire un []map[string]interface{}, Mais je ne vois pas comment faire cela avec l'API car la fonction Rows.Scan() a besoin d'un numéro spécifique de paramètres correspondant au nombre de colonnes demandé (et éventuellement aux types également) pour obtenir correctement les données.

Encore une fois, je voudrais généraliser cet appel et prendre n'importe quelle requête et la transformer en un []map[string]interface{}, Où la carte contient des noms de colonne mappés aux valeurs de cette ligne.

C'est probablement très inefficace, et je prévois de changer la structure plus tard afin que interface{} Soit une structure pour un seul point de données.

Comment pourrais-je faire cela en utilisant uniquement le package base de données/sql, ou si nécessaire le package base de données/sql/driver?

24
lucidquiet

Regardez l'utilisation de sqlx , ce qui peut le faire un peu plus facilement que la base de données standard/bibliothèque sql:

places := []Place{}
err := db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC")
if err != nil {
    fmt.Printf(err)
    return
}

Vous pouvez évidemment remplacer []Place{} avec un []map[string]interface{}, mais dans la mesure du possible, il est préférable d'utiliser une structure si vous connaissez la structure de votre base de données. Vous n'aurez pas besoin d'entreprendre des assertions de type comme vous le feriez sur un interface{}.

14
elithrar

Je ne l'ai pas utilisé (encore), mais je pense que la façon "courante" de faire ce que vous demandez (plus ou moins) est d'utiliser gorp .

4
Ask Bjørn Hansen

Vous pouvez créer une structure qui maintient la clé de mappage à la position de la tranche d'interface [] {}. En faisant cela, vous n'avez pas besoin de créer une structure prédéfinie. Par exemple:

IDOrder: 0
IsClose: 1
IsConfirm: 2
IDUser: 3

Ensuite, vous pouvez l'utiliser comme ceci:

  // create a fieldbinding object.
  var fArr []string
  fb := fieldbinding.NewFieldBinding()

  if fArr, err = rs.Columns(); err != nil {
    return nil, err
  }

  fb.PutFields(fArr)

  //
  outArr := []interface{}{}

  for rs.Next() {
    if err := rs.Scan(fb.GetFieldPtrArr()...); err != nil {
      return nil, err
    }

    fmt.Printf("Row: %v, %v, %v, %s\n", fb.Get("IDOrder"), fb.Get("IsConfirm"), fb.Get("IDUser"), fb.Get("Created"))
    outArr = append(outArr, fb.GetFieldArr())
  }

Exemple de sortie:

Row: 1, 1, 1, 2016-07-15 10:39:37 +0000 UTC
Row: 2, 1, 11, 2016-07-15 10:42:04 +0000 UTC
Row: 3, 1, 10, 2016-07-15 10:46:20 +0000 UTC
SampleQuery: [{"Created":"2016-07-15T10:39:37Z","IDOrder":1,"IDUser":1,"IsClose":0,"IsConfirm":1},{"Created":"2016-07-15T10:42:04Z","IDOrder":2,"IDUser":11,"IsClose":0,"IsConfirm":1},{"Created":"2016-07-15T10:46:20Z","IDOrder":3,"IDUser":10,"IsClose":0,"IsConfirm":1}]

Veuillez voir l'exemple complet ci-dessous ou sur fieldbinding :

main.go

package main

import (
    "bytes"
    "database/sql"
    "encoding/json"
    "fmt"
)

import (
    _ "github.com/go-sql-driver/mysql"
    "github.com/junhsieh/goexamples/fieldbinding/fieldbinding"
)

var (
    db *sql.DB
)

// Table definition
// CREATE TABLE `salorder` (
//   `IDOrder` int(10) unsigned NOT NULL AUTO_INCREMENT,
//   `IsClose` tinyint(4) NOT NULL,
//   `IsConfirm` tinyint(4) NOT NULL,
//   `IDUser` int(11) NOT NULL,
//   `Created` datetime NOT NULL,
//   `Changed` datetime NOT NULL,
//   PRIMARY KEY (`IDOrder`),
//   KEY `IsClose` (`IsClose`)
// ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

func main() {
    var err error

    // starting database server
    db, err = sql.Open("mysql", "Username:Password@tcp(Host:Port)/DBName?parseTime=true")

    if err != nil {
        panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
    }

    defer db.Close()

    // SampleQuery
    if v, err := SampleQuery(); err != nil {
        fmt.Printf("%s\n", err.Error())
    } else {
        var b bytes.Buffer

        if err := json.NewEncoder(&b).Encode(v); err != nil {
            fmt.Printf("SampleQuery: %v\n", err.Error())
        }

        fmt.Printf("SampleQuery: %v\n", b.String())
    }
}

func SampleQuery() ([]interface{}, error) {
    param := []interface{}{}

    param = append(param, 1)

    sql := "SELECT "
    sql += "  SalOrder.IDOrder "
    sql += ", SalOrder.IsClose "
    sql += ", SalOrder.IsConfirm "
    sql += ", SalOrder.IDUser "
    sql += ", SalOrder.Created "
    sql += "FROM SalOrder "
    sql += "WHERE "
    sql += "IsConfirm = ? "
    sql += "ORDER BY SalOrder.IDOrder ASC "

    rs, err := db.Query(sql, param...)

    if err != nil {
        return nil, err
    }

    defer rs.Close()

    // create a fieldbinding object.
    var fArr []string
    fb := fieldbinding.NewFieldBinding()

    if fArr, err = rs.Columns(); err != nil {
        return nil, err
    }

    fb.PutFields(fArr)

    //
    outArr := []interface{}{}

    for rs.Next() {
        if err := rs.Scan(fb.GetFieldPtrArr()...); err != nil {
            return nil, err
        }

        fmt.Printf("Row: %v, %v, %v, %s\n", fb.Get("IDOrder"), fb.Get("IsConfirm"), fb.Get("IDUser"), fb.Get("Created"))
        outArr = append(outArr, fb.GetFieldArr())
    }

    if err := rs.Err(); err != nil {
        return nil, err
    }

    return outArr, nil
}

package de liaison de champ:

package fieldbinding

import (
    "sync"
)

// NewFieldBinding ...
func NewFieldBinding() *FieldBinding {
    return &FieldBinding{}
}

// FieldBinding is deisgned for SQL rows.Scan() query.
type FieldBinding struct {
    sync.RWMutex // embedded.  see http://golang.org/ref/spec#Struct_types
    FieldArr     []interface{}
    FieldPtrArr  []interface{}
    FieldCount   int64
    MapFieldToID map[string]int64
}

func (fb *FieldBinding) put(k string, v int64) {
    fb.Lock()
    defer fb.Unlock()
    fb.MapFieldToID[k] = v
}

// Get ...
func (fb *FieldBinding) Get(k string) interface{} {
    fb.RLock()
    defer fb.RUnlock()
    // TODO: check map key exist and fb.FieldArr boundary.
    return fb.FieldArr[fb.MapFieldToID[k]]
}

// PutFields ...
func (fb *FieldBinding) PutFields(fArr []string) {
    fCount := len(fArr)
    fb.FieldArr = make([]interface{}, fCount)
    fb.FieldPtrArr = make([]interface{}, fCount)
    fb.MapFieldToID = make(map[string]int64, fCount)

    for k, v := range fArr {
        fb.FieldPtrArr[k] = &fb.FieldArr[k]
        fb.put(v, int64(k))
    }
}

// GetFieldPtrArr ...
func (fb *FieldBinding) GetFieldPtrArr() []interface{} {
    return fb.FieldPtrArr
}

// GetFieldArr ...
func (fb *FieldBinding) GetFieldArr() map[string]interface{} {
    m := make(map[string]interface{}, fb.FieldCount)

    for k, v := range fb.MapFieldToID {
        m[k] = fb.FieldArr[v]
    }

    return m
}
0
Jun Xie

Si vous n'avez vraiment pas de carte, ce qui est nécessaire dans certains cas, jetez un œil à dbr, mais vous devez utiliser le fork (car le pr a été rejeté dans le dépôt d'origine). La fourche semble quand même plus à jour:

https://github.com/mailru/dbr

Pour plus d'informations sur son utilisation:

https://github.com/gocraft/dbr/issues/8

0
Simon Meusel