database 从数据库行在 Golang 中创建地图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17840963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 09:01:12  来源:igfitidea点击:

Create a Map in Golang from database Rows

databasegovariadic-functions

提问by lucidquiet

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.

基本上在执行查询之后,我想获取结果行并生成一个[]map[string]interface{},但我不知道如何使用 API 执行此操作,因为该Rows.Scan()函数需要特定数量的参数与请求的列数(可能还有类型)匹配以及)以正确获取数据。

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.

同样,我想概括这个调用,并接受任何查询并将其转换为[]map[string]interface{},其中映射包含映射到该行值的列名。

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

这可能非常低效,我计划稍后更改结构,以便它interface{}是单个数据点的结构。

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

我将如何仅使用 database/sql 包或在必要时使用 database/sql/driver 包来执行此操作?

采纳答案by elithrar

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

看看使用sqlx,它可以比标准的数据库/sql库更容易地做到这一点:

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{}.

您显然可以[]Place{}用 a替换[]map[string]interface{},但如果您知道数据库的结构,最好使用 struct 。您不需要像在interface{}.

回答by Ask Bj?rn Hansen

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.

我还没有使用它(还),但我相信做你所要求的(或多或少)的“常见”方法是使用gorp

回答by Jun Xie

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:

您可以创建一个结构体来维护 []interface{} 切片位置的映射键。通过这样做,您不需要创建预定义的结构。例如:

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:

请参阅下面或fieldbinding的完整示例:

main.go

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
}

回答by Simon Meusel

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:

如果你真的不需要地图,这在某些情况下是需要的,看看 dbr,但你需要使用 fork(因为 pr 在原始 repo 中被拒绝)。无论如何,叉子似乎更新了:

https://github.com/mailru/dbr

https://github.com/mailru/dbr

For info on how to use it:

有关如何使用它的信息:

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

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

回答by user11297483

    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]

&map[col1:abc col2:123]