Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/78.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
是否可以使用GoLang数据库/sql按名称检索列值_Sql_Go - Fatal编程技术网

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

是否可以使用GoLang数据库/sql按名称检索列值,sql,go,Sql,Go,我看到的所有使用sql.Row,通过position访问查询返回值的示例:sql.Rows.scan()需要在scan()参数中正确地定位一个正确键入的变量,以检索返回的每个列值,例如,在以下示例中: 必须正确定位行的&name和&age列(第0列和第1列)。Scan()以使用正确的类型检索正确的值 在我多年的生产系统开发过程中,我一直在刻意避免这种做法,因为这种做法并不可靠:如果基于列位置对列布局进行数据库更改,则很容易破坏代码 使用列名来检索值要健壮得多-这将使您免受数据库更改的影响,这些

我看到的所有使用
sql.Row
,通过position访问查询返回值的示例:
sql.Rows.scan()
需要在
scan()
参数中正确地定位一个正确键入的变量,以检索返回的每个列值,例如,在以下示例中:

必须正确定位行的
&name
&age
列(第0列和第1列)。Scan()以使用正确的类型检索正确的值

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

使用列名来检索值要健壮得多-这将使您免受数据库更改的影响,这些更改会添加或删除列,从而导致基于位置的代码出错。例如,在Delphi和C#中,所有数据集,包括从查询返回值的列,都支持
FieldByName('age')。asInteger
字段['age']。value,

有没有办法在围棋中完成这项任务?如果没有,这是Go数据库支持的一个大缺点,也是一个严重的失望——正如前面提到的,一点也不安全

编辑:

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


假设在锁定的数据库中有一个实用程序查询,我无法修改或添加该查询,它检索了多个列,但我当前的任务只需要其中一个列。基于当前的
sql.Rows.Scan()
模型,我必须从我的应用程序代码中的查询中检索所有值,即使我不需要它们,但是如果我可以查询
“columnByName”
,那就没有必要了-我可以将需要的数据带到我的应用程序代码中。对此有何解决方法?

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

命名匹配确实有轻微的损失。命名匹配与自己匹配列位置没有什么不同。它只是在运行时为您完成这项工作——可能是在每次查询执行时。这在任何其他语言中都是正确的

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

如果你要建立自己的图书馆,你自己怎么做

  • 获取列名和位置
  • 传递一段指针
    []接口{}
    以获取值

  • 并获取指向目标值的指针

  • 如果要映射到结构字段,请获取结构字段的
好的,让我们看看它是如何工作的

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)
}

唯一明智且干净的方法是使用:

假设您有一个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)
}

更多信息:

对于,通过列名访问的实用程序函数并不难实现。@dystroy-AFAIK,不是真的:我在考虑使用列名作为键、列值作为值的映射。但是,如果DB模式发生更改,底层scan()调用仍将依赖于位置和中断。也许您正在考虑其他事情?按名称获取字段并不能真正保护您免受所有数据库更改的影响。您的代码的一些集成测试会更加健壮。@alpe1-不确定您的观点是什么…@ComeAndGo每当我运行mysql查询时,我都会实现一个函数,该函数基本上将数据打包到字符串到字符串的映射切片中([]map[string]string)。每个切片都相当于一行,映射键字符串是该行的列名,映射值字符串是该行的列值。下面是一个简单的示例,演示它是如何工作的,并共享packageData函数代码:这是如何不受本机支持的?列名是动态的。Go是静态编译的。因此,您必须在运行时匹配名称。有些库可以为您执行此操作。这个答案说明了它是如何完成的。列名从来都不是动态的,您必须重新编译代码才能更改sql stmt/列名。我只是对Go自动取款机里有这么多PITA感到惊讶。即使使用sqlx,这也很棒。列名存储在字符串变量中,可以在外部填充,而无需重新编译。这使得它们不是静态的。结构字段名是静态的。更改这些内容需要重新编译。即使在这种极端情况下,也只有通过列名而不是列位置访问结果才有意义
rows, err := db.Queryx("SELECT * FROM place")
for rows.Next() {
    var p Place
    err = rows.StructScan(&p)
}