一尘不染

是否可以使用GoLang数据库/ 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)
}

&name并且&age必须正确放置Rows.Scan()的位置(0和1列),以检索具有正确类型的正确值。

在生产系统的多年开发中,我一直在避免这种做法,因为这种做法不够可靠:如果数据库的布局基于列位置,则更改列布局会很容易破坏您的代码。

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

有什么办法可以在Go中做到这一点?如果不是这样,这将是Go数据库支持中的一个很大缺点,并且会令人严重失望-完全不安全,如上所述。

编辑:

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

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


阅读 224

收藏
2020-07-02

共1个答案

一尘不染

是的,可以这样做,而不必手动匹配列位置。您可以使用一些第三方库来执行此操作,例如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)
}
2020-07-02