You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang中如何将SQL查询行追加到列表且不覆盖原有数据?

解决SQL行转JSON时重复最后一行的问题

嘿,我一眼就看出你遇到的是Go里处理SQL结果集时常见的引用复用陷阱,咱们一步步拆解问题,再给你修正后的代码:

原代码的核心问题

  1. 错误绑定了列名的地址:你把vals[i] = &cols[i],这相当于让每个值都指向列名字符串的内存地址,而不是用来存储当前行数据的独立变量。每次rows.Scan都会把数据写到这些地址上,最后所有行都共享同一批地址,自然全是最后一行的数据。
  2. 复用了同一个map引用:你的m是在rows.Next()循环外面创建的,每次循环只是修改这个map里的键值对,而list中添加的都是这个map的引用。最后list里的所有元素其实都是同一个map,显示的当然是最后一次修改的结果。
  3. 额外小问题:重复调用了rows.Columns(),完全没必要,一次就够了。

修正后的完整代码

import (
    "encoding/json"
    "log"
    "database/sql"
    _ "你的数据库驱动包" // 比如_ "github.com/go-sql-driver/mysql"
)

func main() {
    // 假设你已经初始化了db连接
    query := "SELECT ... FROM ..." // 你的查询语句

    rows, err := db.Query(query)
    if err != nil {
        log.Fatalf("查询失败: %v", err)
    }
    defer rows.Close() // 务必关闭rows,避免数据库资源泄漏

    // 只获取一次列名
    colnames, err := rows.Columns()
    if err != nil {
        log.Fatalf("获取列名失败: %v", err)
    }

    list := make([]map[string]interface{}, 0)

    // 遍历每一行数据
    for rows.Next() {
        // 每次循环创建新的vals切片,存储当前行每个字段值的地址
        vals := make([]interface{}, len(colnames))
        // 为每个字段创建独立的interface{}变量,把地址存入vals
        for i := range colnames {
            var val interface{}
            vals[i] = &val
        }

        // 扫描当前行数据到vals对应的地址中
        if err := rows.Scan(vals...); err != nil {
            log.Fatalf("扫描行数据失败: %v", err)
        }

        // 每次循环创建新的map,避免引用复用
        rowMap := make(map[string]interface{})
        for i, valPtr := range vals {
            // 解引用拿到实际的字段值
            rowMap[colnames[i]] = *valPtr.(*interface{})
        }

        // 将当前行的map加入列表
        list = append(list, rowMap)
    }

    // 检查遍历过程中是否出现错误
    if err := rows.Err(); err != nil {
        log.Fatalf("遍历行数据失败: %v", err)
    }

    // 转换为JSON
    jsonBytes, err := json.Marshal(list)
    if err != nil {
        log.Fatalf("JSON序列化失败: %v", err)
    }

    log.Println(string(jsonBytes))
}

关键修改说明

  • 每次循环创建新的vals和map:确保每一行的数据都存在独立的内存空间,不会被下一行覆盖。
  • 正确创建数据存储变量:用var val interface{}生成独立变量,让rows.Scan把当前行的字段值写入这个变量,而不是覆盖列名。
  • 解引用获取实际值:vals里存的是*interface{}类型的指针,所以需要通过*valPtr.(*interface{})拿到实际的字段值存入map。
  • 完善错误处理和资源释放:补充了defer rows.Close()和rows.Err()检查,这是Go操作SQL结果集的标准做法,能避免资源泄漏和隐藏的遍历错误。

内容的提问来源于stack exchange,提问作者sibert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:19:45