doupike2351 2018-04-28 09:46
浏览 56
已采纳

如何在不替换Golang中前一行的情况下将最后一个sql行添加到列表中

This code delivers AFAIK correct JSON output [{},{}], but each row is appended and replaces all previous rows, so the result shows only copies of the last row.

var rows *sql.Rows
rows, err = db.Query(query)
cols, _ := rows.Columns()
colnames, _ := rows.Columns()
vals := make([]interface{}, len(cols))

for i, _ := range cols {
   vals[i] = &cols[i]
}

m := make(map[string]interface{})

for i, val := range vals {
  m[colnames[i]] = val
}

list := make([]map[string]interface{}, 0)
for rows.Next() {
err = rows.Scan(vals...)
   list = append(list, m)
}
json, _ := json.Marshal(list)
fmt.Fprintf(w,"%s
", json)

This is what happens behind the scenes looping through the rows:

loop 1: {“ID”:“1”,“NAME”: "John }

loop 2: {“ID”:“2”,“NAME”: “Jane Doe”}{“ID”:“2”,“NAME”: “Jane Doe”}

loop 3: {“ID”:“3”,“NAME”: “Donald Duck”}{“ID”:“3”,“NAME”: “Donald Duck”}{“ID”:“3”,“NAME”: “Donald Duck”}

The rows.Scan fetches the correct values, but it appends AND replaces all previous values.

The final output is this

[{“ID”:“3”,“NAME”: “Donald Duck”},{“ID”:“3”,“NAME”: “Donald Duck”},{“ID”:“3”,“NAME”: “Donald Duck”}]

But should be this:

[{“ID”:“1”,“NAME”: “John Doe”},{“ID”:“2”,“NAME”: “Jane Doe”},{“ID”:“3”,“NAME”: “Donald Duck”}]

What am I doing wrong?

You may downvote this, but please explain why. I am still a newbie on Golang and want to learn.

  • 写回答

1条回答 默认 最新

  • dqyin0101 2018-04-28 10:36
    关注

    I fixed it and explained with comments what you did wrong:

    // 1. Query
    var rows *sql.Rows
    rows, err = db.Query(query)
    cols, _ := rows.Columns()
    
    // 2. Iterate
    list := make([]map[string]interface{}, 0)
    for rows.Next() {
        vals := make([]interface{}, len(cols))
        for i, _ := range cols {
            // Previously you assigned vals[i] a pointer to a column name cols[i].
            // This meant that everytime you did rows.Scan(vals),
            // rows.Scan would see pointers to cols and modify them
            // Since cols are the same for all rows, they shouldn't be modified.
    
            // Here we assign a pointer to an empty string to vals[i],
            // so rows.Scan can fill it.
            var s string
            vals[i] = &s
    
            // This is effectively like saying:
            // var string1, string2 string
            // rows.Scan(&string1, &string2)
            // Except the above only scans two string columns
            // and we allow as many string columns as the query returned us — len(cols).
        }
    
        err = rows.Scan(vals...)
    
        // Don't forget to check errors.
        if err != nil {
            log.Fatal(err)
        }
    
        // Make a new map before appending it.
        // Remember maps aren't copied by value, so if we declared
        // the map m outside of the rows.Next() loop, we would be appending
        // and modifying the same map for each row, so all rows in list would look the same.
        m := make(map[string]interface{})
        for i, val := range vals {
            m[cols[i]] = val
        }
        list = append(list, m)
    }
    
    // 3. Print.
    b, _ := json.MarshalIndent(list, "", "\t")
    fmt.Printf("%s
    ", b)
    

    Don't worry, this was hard for me to understand when I was a beginner as well.

    Now, something fun:

    var list []map[string]interface{}
    rows, err := db.Queryx(query)
    for rows.Next() {
        row := make(map[string]interface{})
        err = rows.MapScan(row)
        if err != nil {
          log.Fatal(err)
        }
        list = append(list, row)
    }
    
    b, _ := json.MarshalIndent(list, "", "\t")
    fmt.Printf("%s
    ", b)
    

    This does the same as the code above it, but with sqlx. A bit simpler, no?

    sqlx is an extension on top of database/sql with methods to scan rows directly to maps and structs, so you don't have to do that manually.

    I think your model looks nicer as a struct:

    type Person struct {
        ID int
        Name string
    }
    
    var people []Person
    rows, err := db.Queryx(query)
    for rows.Next() {
        var p Person
        err = rows.StructScan(&p)
        if err != nil {
            log.Fatal(err)
        }
        people = append(people, p)
    }
    

    Don't you think?

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 远程桌面文档内容复制粘贴,格式会变化
  • ¥15 关于#java#的问题:找一份能快速看完mooc视频的代码
  • ¥15 这种微信登录授权 谁可以做啊
  • ¥15 请问我该如何添加自己的数据去运行蚁群算法代码
  • ¥20 用HslCommunication 连接欧姆龙 plc有时会连接失败。报异常为“未知错误”
  • ¥15 网络设备配置与管理这个该怎么弄
  • ¥20 机器学习能否像多层线性模型一样处理嵌套数据
  • ¥20 西门子S7-Graph,S7-300,梯形图
  • ¥50 用易语言http 访问不了网页
  • ¥50 safari浏览器fetch提交数据后数据丢失问题