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

时间:2014-02-24 11:44:34

标签: sql go

我见过的所有使用sql.Row的示例,通过位置访问查询的返回值:sql.Rows.scan()需要正确输入正确的变量定位< / strong>在对应于相应列的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)
} 
对于Rows.Scan(),必须正确定位<&name&age以及FieldByName('age').asInteger(第0列和第1列)才能使用正确的类型检索正确的值。

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

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

任何在Go中完成此任务的方法?如果没有,这是Go数据库支持的一个很大的缺点,并且非常令人失望 - 如上所述,根本不是安全的。

修改

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

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

2 个答案:

答案 0 :(得分: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)
}

答案 1 :(得分:0)

执行此操作的唯一理智而干净的方法是使用:https://github.com/jmoiron/sqlx

假设您有一个Place结构:

type Place struct {
    Country       string
    City          sql.NullString
    TelephoneCode int `db:"telcode"`
}

您可以轻松扫描它:

rows, err := db.Queryx("SELECT * FROM place")
for rows.Next() {
    var p Place
    err = rows.StructScan(&p)
}

更多信息:http://jmoiron.github.io/sqlx/