【问题标题】:Scanning large rows into structs in Go在 Go 中将大行扫描到结构中
【发布时间】:2017-05-10 16:37:37
【问题描述】:

我正在使用一个尚未标准化的数据库,该表包含超过 40 列的记录。

以下是我的 Go 代码,用于(尝试)将记录扫描到一个大型结构中:

type Coderyte struct {
  ID int `json:"id"`
  EmrID int `json:"emr_id"`
  DftID int `json:"dft_id"`

  Fix int `json:"fix"`
  ReportDate string `json:"report_date"` // Time?
  Patient string `json:"patient"`
  ... // etc
}
func ReadCoderyte(res http.ResponseWriter, req *http.Request) {
  rows, err := db.Query("SELECT * FROM coderyte")
  if err != nil {
    http.Error(res, "Error querying database", 500)
  }
  defer rows.Close()

  // Convert rows into a slice of Coderyte structs
  coderytes := make([]*Coderyte, 0)
  for rows.Next() {
    coderyte := new(Coderyte)
    err := rows.Scan(&coderyte) // Expected 42 columns
    if err != nil {
      panic(err)
      http.Error(res, "Error converting coderyte object", 500)
    }
    coderytes = append(coderytes, coderyte)
  }

当我调用此代码时,Scan 抱怨它“需要 42 个目标参数,而不是 1 个”。我的理解是,我需要在扫描调用内部处理这个大型结构中的每个字段,即Scan(&coderyte.ID, &coderyte.EmrID, etc)

我的搜索只产生了this other question,建议的答案是使用sqlx。如果不需要,我会尽量避免使用第三方工具。

我的问题归结为:有没有办法在不指定每个字段的情况下将大型数据库记录转换为结构?

我还应该注意,这个函数的最终目标是返回一个 JSON 对象数组,但我没有包含那部分代码,因为我觉得它并不重要。如果有办法绕过Scan 并返回 JSON,那也是一个值得赞赏的答案。

【问题讨论】:

  • 简短回答:不。长答案:我也推荐 sqlx。标准库不支持解组为结构。
  • 我也有同样的建议。
  • 是的,不支持,但如果你仍然想使用标准的 sql 包,我建议阅读go-database-sql.org/retrieving.html
  • RE:您问题的第二部分:我制作了一个小包,我在所有地方都使用了这个包。如果您想检查一下,这非常简单,我在这里总结了一下:gist.github.com/heyimalex/075bb442af0ecbf3e5f561336101e4f7
  • @AlexGuerra 必须为sqlx 制作和标记结构可能很烦人,但利用该方法的类型安全性有其优势。

标签: json database go struct


【解决方案1】:

这个函数的最终目标是返回一个 JSON 对象数组

听起来你可以完全绕过结构,而是扫描到map[string]interface{},然后非常动态地完成这一切: 你可以这样做:

rows, _ := db.Query("SELECT * FROM coderyte") 
cols, _ := rows.Columns()
store := []map[string]interface{}
for rows.Next() {
    columns := make([]interface{}, len(cols))
    columnPointers := make([]interface{}, len(cols))
    for i, _ := range columns {
        columnPointers[i] = &columns[i]
    }

    if err := rows.Scan(columnPointers...); err != nil {
        return err
    }
    m := make(map[string]interface{})
    for i, colName := range cols {
        val := columnPointers[i].(*interface{})
        m[colName] = *val
    }
    store = append(store, m)        
}
js, _ := json.Marshal(store)
fmt.Println(string(js))

现在,显然您也可以将其转换为结构,因为您可以获取 json 并执行 json.Unmarshal,但考虑到您的用例,这似乎是一个毫无意义的额外步骤。

js, _ := json.Marshal(store)
structs := []Coderyte{}
json.Unmarshal(js, &structs)

话虽如此,您可能应该只使用 sqlx - 他们可能会做更聪明的事情并且更有效地做事。

【讨论】:

  • 我刚刚和我的老板谈过,他透露他一直在使用sqlx 来修补相同的数据。我将尝试这个解决方案,因为现在我只想要数据的 JSON 表示,但我可能会根据其他 cmets 和您的建议转移到 sqlx。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-15
  • 2020-08-25
  • 2020-09-26
  • 2016-05-22
  • 2019-05-20
相关资源
最近更新 更多