Create a Map in Golang from database Rows

╄→尐↘猪︶ㄣ 提交于 2019-12-17 22:47:32

问题


Basically after doing a query I'd like to take the resulting rows and produce a []map[string]interface{}, but I do not see how to do this with the API since the Rows.Scan() function needs a specific number of parameters matching the requested number of columns (and possibly the types as well) to correctly obtain the data.

Again, I'd like to generalize this call and take any query and turn it into a []map[string]interface{}, where the map contains column names mapped to the values for that row.

This is likely very inefficient, and I plan on changing the structure later so that interface{} is a struct for a single data point.

How would I do this using just the database/sql package, or if necessary the database/sql/driver package?


回答1:


Look at using sqlx, which can do this a little more easily than the standard database/sql library:

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

You could obviously replace []Place{} with a []map[string]interface{}, but where possible it is better to use a struct if you know the structure of your database. You won't need to undertake any type assertions as you might on an interface{}.




回答2:


I haven't used it (yet), but I believe the "common" way to do what you are asking (more or less) is to use gorp.




回答3:


You can create a struct that maintains the map key to the position of the []interface{} slice. By doing this, you do not need to create a predefined struct. For example:

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

Then, you can use it like this:

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

Sample output:

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

Please see the full example below or at 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
}

fieldbinding package:

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
}



回答4:


If you really wan't a map, which is needed in some cases, have a look at dbr, but you need to use the fork (since the pr got rejected in the original repo). The fork seems more up to date anyway:

https://github.com/mailru/dbr

For info on how to use it:

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




回答5:


    package main

import (
    "fmt"
    "github.com/bobby96333/goSqlHelper"
)

func main(){
    fmt.Println("hello")
    conn,err :=goSqlHelper.MysqlOpen("user:password@tcp(127.0.0.1:3306)/dbname")
    checkErr(err)
    row,err := conn.QueryRow("select * from table where col1 = ? and  col2 = ?","123","abc")
    checkErr(err)
    if *row==nil {
        fmt.Println("no found row")
    }else{
        fmt.Printf("%+v",row)
    }
}

func checkErr(err error){
    if err!=nil {
        panic(err)
    }
}

output:

&map[col1:abc col2:123]



来源:https://stackoverflow.com/questions/17840963/create-a-map-in-golang-from-database-rows

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!