是否可以使用GoLang数据库/ sql按名称检索列值

Vec*_*tor 10 sql go

我看到的所有使用的示例sql.Row,按位置访问查询的返回值:sql.Rows.scan()需要在对应于相应列的参数中正确定位的正确类型变量scan(),以检索返回的每个列值,例如在以下示例中:

基于GoDocs的示例(使用小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)
} 
Run Code Online (Sandbox Code Playgroud)

&name并且&age必须正确定位(第0列和第1列)以便Rows.Scan()使用正确的类型检索正确的值.

在我多年的生产系统开发中,我已经刻意避免这种做法,因为它不健壮:如果基于列位置,列的布局中的数据库更改将很容易破坏您的代码.

使用列名来检索值会更加健壮- 这可以使您免受对添加或删除会破坏基于位置的代码的列的数据库的更改.例如,在Delphi和C#,所有数据集,包括从查询返回值的列,支持FieldByName('age').asIntegerfields['age'].value,

在Go中有什么方法可以做到这一点?如果没有,这是Go数据库支持的一个很大的缺点,并且非常令人失望 - 如上所述,根本不安全.

编辑:

另外(也许这是一个新问题):我看到的示例似乎要求您检索查询返回的所有列,否则列的位置将会偏斜.

假设在锁定的数据库中存在我无法修改或添加的实用程序查询,并且它检索了多个列,但我只需要其中一个用于当前任务.基于当前sql.Rows.Scan()模型,我必须在我的应用程序代码中检索查询中的所有值,即使我不需要它们,而如果我可以查询"columnByName"那些没有必要 - 我可以将我的应用程序代码引入我需要的数据.为此做任何解决方法?

Luk*_*uke 15

是的,可以在不必手动匹配列位置的情况下执行此操作.您可以使用一些第三方库来执行此操作,例如sqlxgorp.我建议坚持使用其中一种而不是自己动手.

命名匹配确实有轻微的惩罚.命名匹配与自己匹配列位置没有什么不同.它只是在运行时为您工作 - 可能在每次查询执行时.在任何其他语言中都是如此.

为什么在运行时?查询被写为字符串.必须对其进行解析以确定位置.

如果你要建立自己的图书馆,你如何自己做?

好的,让我们看看它是如何工作的.

type Person struct {
    Id int
    Name string
}
rows, err := db.Query("SELECT id, name FROM person;")
if err != nil {
    // handle err
    log.Fatal(err)
}
columnNames, err := rows.Columns() // []string{"id", "name"}
if err != nil {
    // handle err
    log.Fatal(err)
}
people = make([]Person, 0, 2)
for rows.Next() {
    person := Person{}
    // person == Person{0, ""}
    pointers := make([]interface{}, len(columnNames))
    // pointers == `[]interface{}{nil, nil}`
    structVal := reflect.ValueOf(person)
    for i, colName := range columnNames {
        fieldVal := structVal.FieldByName(strings.Title(colName))
        if !fieldVal.IsValid() {
            log.Fatal("field not valid")
        }
        pointers[i] = fieldVal.Addr().Interface()
    }
    // pointers == `[]interface{}{&int, &string}`
    err := rows.Scan(pointers...)
    if err != nil {
        // handle err
        log.Fatal(err)
    }
    // person == Person{1, "John Doe"}
    people = append(people, person)
}
Run Code Online (Sandbox Code Playgroud)

  • 这怎么不是原生支持的? (3认同)
  • 列名永远不是动态的,你必须重新编译代码才能改变你的sql stmt/column名称.我只是惊讶于它在Go atm中有多少PITA.即使使用sqlx,这也很棒. (3认同)
  • 列名存储在字符串var中,可以在外部进行填充而无需重新编译。这使得它们不是静态的。结构字段名称是静态的。更改这些要求重新编译。 (2认同)
  • 即使在这种极端情况下,通过列名称而不是列位置访问结果是唯一有意义的事情 (2认同)