在Golang中从数据库行创建一个Map。

huangapple go评论98阅读模式
英文:

Create a Map in Golang from database Rows

问题

基本上,在执行查询之后,我想要获取结果行并生成一个[]map[string]interface{},但是我不知道如何在API中实现这一点,因为Rows.Scan()函数需要与请求的列数(可能还包括类型)匹配的特定数量的参数来正确获取数据。

再次强调,我想要将任何查询都通用化,并将其转换为一个[]map[string]interface{},其中map将列名映射到该行的值。

这可能非常低效,我计划稍后更改结构,使interface{}成为单个数据点的结构体。

如何仅使用database/sql包,或者如果必要的话使用database/sql/driver包来实现这一点?

英文:

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

得分: 12

使用sqlx来完成这个任务会比标准的database/sql库更容易一些:

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

当然,你可以将[]Place{}替换为[]map[string]interface{},但如果你知道数据库的结构,最好使用struct。这样你就不需要像在interface{}上那样进行任何类型断言。

英文:

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

得分: 2

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

现在10年后:是的,不要这样做!使用 sqlc 或 sqlx。

英文:

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.

Now 10 years later: Yeah, don't do that! Use sqlc or sqlx.

答案3

得分: 2

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

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

然后,您可以像这样使用它:

  // 创建一个fieldbinding对象。
  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())
  }

示例输出:

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

请参阅下面的完整示例或这里

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
)

// 表定义
// 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

	// 启动数据库服务器
	db, err = sql.Open("mysql", "Username:Password@tcp(Host:Port)/DBName?parseTime=true")

	if err != nil {
		panic(err.Error()) // 仅用于示例目的。您应该使用适当的错误处理,而不是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()

	// 创建一个fieldbinding对象。
	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 fieldbinding

import (
	"sync"
)

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

// FieldBinding用于SQL rows.Scan()查询。
type FieldBinding struct {
	sync.RWMutex // 嵌入。参见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:检查map键是否存在和fb.FieldArr边界。
	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
}
英文:

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

得分: -1

如果你真的需要一个地图,在某些情况下是必需的,可以看看dbr,但你需要使用分支(因为原始仓库中的pr被拒绝了)。这个分支似乎更加更新:

https://github.com/mailru/dbr

关于如何使用它的信息:

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

英文:

If you really want 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

得分: -1

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]

英文:
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]

huangapple
  • 本文由 发表于 2013年7月25日 01:32:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/17840963.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定