【发布时间】:2014-02-24 11:44:34
【问题描述】:
我见过的所有使用 sql.Row 的示例,通过 位置 访问查询的返回值:sql.Rows.scan() 需要正确定位中的正确类型变量scan() 参数对应于相应的列,以检索返回的每个列值,例如在以下示例中:
Example Based on GoDocs (with small mod):
rows, err := db.Query("SELECT name,age FROM users WHERE age>=50")
if err != nil {
log.Fatal(err)
}
for rows.Next() {
var name string
var age int
if err := rows.Scan(&name,&age); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
&name 和 &age 必须正确定位(第 0 列和第 1 列),Rows.Scan() 才能检索具有正确类型的正确值。
在我多年的生产系统开发中,我已经开始刻意避免这种做法,因为它不可靠:如果数据库更改列布局,如果它基于列位置,很容易破坏您的代码。
使用列名 来检索值要可靠得多 - 这可以使您免受数据库更改的影响,这些更改会添加或删除会破坏基于位置的代码的列。例如,在 Delphi 和 C# 中,所有的数据集,包括从查询返回值的列,都支持FieldByName('age').asInteger 或fields['age'].value, 等。
在 Go 中有什么方法可以做到这一点?如果不是,这将是 Go 数据库支持的一个大缺点,并且令人严重失望 - 如上所述,这根本不安全。
编辑:
还有(也许这是一个新问题):我看到的例子似乎要求你检索查询返回的所有列,否则列的位置会倾斜。
假设在一个锁定的数据库中有一个我无法修改或添加的实用程序查询,它检索了几列,但我现在的任务只需要其中一个。基于当前的sql.Rows.Scan() 模型,我必须从我的应用程序代码中的查询中检索所有值,即使我不需要它们,而如果我可以查询"columnByName",那就没有必要了——我可以将我需要的数据带入我的应用程序代码。有什么解决方法吗?
【问题讨论】:
-
有了column names,一个通过列名访问的实用函数将不难实现。
-
@dystroy - AFAIK,不是真的:我正在考虑使用列名作为键和列值作为值的映射。但是如果数据库模式发生变化,底层的 scan() 调用仍将依赖于位置和中断。也许你在想别的?
-
按名称获取字段并不能真正保护您免受所有数据库更改的影响。对您的代码进行一些集成测试会更加健壮。
-
@alpe1 - 不知道你的意思是什么......
-
@ComeAndGo 我有一个我喜欢在运行 mysql 查询时实现的函数,它基本上将数据打包成字符串到字符串的映射切片 ([]map[string]string) .每个切片相当于一行,映射键字符串是列名,映射值字符串是该行的列值。这里有一个简单的 Playground 来演示它是如何工作的并分享 packageData 函数代码:play.golang.org/p/ctZYCn9EI9