Go SQL扫描的行被覆盖了。

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

Go SQL scanned rows getting overwritten

问题

我正在尝试从 SQL 服务器的表中读取所有行,并将它们存储在字符串切片中以供以后使用。我遇到的问题是,每次扫描新行时,先前扫描的行都被覆盖了,尽管我已经将所有可变的字节切片转换为不可变的字符串,并将结果切片保存到另一个切片中。以下是我使用的代码:

rawResult := make([]interface{}, len(cols)) // 保存行中可能出现的任何内容
result := make([]string, len(cols)) // 将以字符串形式保存所有行元素
var results [][]string // 将保存所有结果字符串切片
dest := make([]interface{}, len(cols)) // 临时切片,用于传递给 scan 函数
for i, _ := range rawResult {
    dest[i] = &rawResult[i] // 用 rawResult 的指针填充 dest,以便传递给 scan 函数
}
for rows.Next() { // 对于每一行
    err = rows.Scan(dest...) // 扫描行
    if err != nil {
        log.Fatal("Failed to scan row", err)
    }
    for i, raw := range rawResult { // 对于行中的每个扫描的字节切片
        switch rawtype := raw.(type){ // 确定类型,转换为字符串
        case int64:
            result[i] = strconv.FormatInt(raw.(int64), 10)
        case float64:
            result[i] = strconv.FormatFloat(raw.(float64), 'f', -1, 64)
        case bool:
            result[i] = strconv.FormatBool(raw.(bool))
        case []byte:
            result[i] = string(raw.([]byte))
        case string:
            result[i] = raw.(string)
        case time.Time:
            result[i] = raw.(time.Time).String()
        case nil:
            result[i] = ""
        default: // 实际上不应该到达这里,因为所有类型都已经被处理了
            log.Fatal("Unexpected type %T", rawtype)
        }
    }
    results = append(results, result) // 将结果追加到结果切片中
}

我确定这与 Go 处理变量和内存的方式有关,但我似乎无法解决它。有人能解释一下我没有理解的地方吗?

英文:

I'm trying to read all the rows from a table on a SQL server and store them in string slices to use for later. The issue I'm running into is that the previously scanned rows are getting overwritten every time I scan a new row, even though I've converted all the mutable byte slices to immutable strings and saved the result slices to another slice. Here is the code I'm using:

rawResult := make([]interface{}, len(cols)) // holds anything that could be in a row
result := make([]string, len(cols)) // will hold all row elements as strings
var results [][]string // will hold all the result string slices
dest := make([]interface{}, len(cols)) // temporary, to pass into scan
for i, _ := range rawResult {
    dest[i] = &rawResult[i] // fill dest with pointers to rawResult to pass into scan
}
for rows.Next() { // for each row
    err = rows.Scan(dest...) // scan the row
    if err != nil {
        log.Fatal("Failed to scan row", err)
    }
    for i, raw := range rawResult { // for each scanned byte slice in a row
        switch rawtype := raw.(type){ // determine type, convert to string
        case int64:
            result[i] = strconv.FormatInt(raw.(int64), 10)
        case float64:
            result[i] = strconv.FormatFloat(raw.(float64), 'f', -1, 64)
        case bool:
            result[i] = strconv.FormatBool(raw.(bool))
        case []byte:
            result[i] = string(raw.([]byte))
        case string:
            result[i] = raw.(string)
        case time.Time:
            result[i] = raw.(time.Time).String()
        case nil:
            result[i] = ""
        default: // shouldn't actually be reachable since all types have been covered
            log.Fatal("Unexpected type %T", rawtype)
        }
    }
    results = append(results, result) // append the result to our slice of results
}

I'm sure this has something to do with the way Go handles variables and memory, but I can't seem to fix it. Can somebody explain what I'm not understanding?

答案1

得分: 1

你应该为每一行数据创建一个新的切片。注意,切片有一个指向底层数组的指针,所以你添加到results中的每个切片都指向同一个实际数据数组的指针。这就是你遇到这种行为的原因。

英文:

You should create new slice for each data row. Notice, that a slice has a pointer to underlying array, so every slice you added into results have same pointer on actual data array. That's why you have faced with that behaviour.

答案2

得分: 1

当你使用make()函数创建一个切片时,它返回的是一个类型(而不是类型的指针)。但它并不会在每次重新分配元素时分配新的内存。因此,使用以下代码:

result := make([]string, 5)

将会有固定的内存来容纳5个字符串。当一个元素被重新分配时,它会占用之前相同的内存,从而覆盖旧值。

希望下面的示例能够解释清楚这个问题。

http://play.golang.org/p/3w2NtEHRuu

因此,在你的程序中,你正在更改相同内存的内容,并且一遍又一遍地进行追加。为了解决这个问题,你应该在循环内部创建你的结果切片。

英文:

When you create a slice using func make() it return a type (Not a pointer to type). But it does not allocate new memory each time a element is reassigned. Hence

result := make([]string, 5)

will have fix memory to contain 5 strings. when a element is reassigned, it occupies same memory as before hence overriding the old value.

Hopefully following example make things clear.

http://play.golang.org/p/3w2NtEHRuu

Hence in your program you are changing the content of the same memory and appending it again and again. To solve this problem you should create your result slice inside the loop.

答案3

得分: 0

result := make([]string, len(cols))移动到循环遍历可用行的for循环中。

英文:

Move result := make([]string, len(cols)) into your for loop that loops over the available rows.

huangapple
  • 本文由 发表于 2015年7月21日 07:50:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/31528168.html
匿名

发表评论

匿名网友

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

确定