根据文档,似乎在Go中从数据库中获取数据的唯一方法是使用Rows.Scan(),这意味着您必须在编译时知道所有列的计数和类型。
我错过了什么吗?你是如何支持即席查询的?或者甚至将所有列从表中拉出来,将来可能会发生变化?
答案 0 :(得分:11)
sql.Rows
类型有一个Columns
方法,可以为您提供结果列名称列表。这可用于确定未知查询的列数。
在Scan
方法的文档中,它说:
如果参数的类型为* [] byte,则Scan会在该参数中保存一份副本 相应的数据。该副本由调用者拥有,可以 修改并无限期地持有。使用一个可以避免副本 改为类型为* RawBytes的参数;请参阅RawBytes的文档 限制其使用。
如果参数的类型为* interface {},则Scan会复制提供的值 没有转换的底层驱动程序。如果值是类型 []字节,复制,调用者拥有结果。
因此,当我们不知道其类型时,我们也支持扫描列值:无论是原始格式还是Go类型。
将这两者放在一起,您可以使用...
syntax to call variadic functions:
columnNames, err := rows.Columns()
if err != nil {
log.Fatalln(err) // or whatever error handling is appropriate
}
columns := make([]interface{}, len(columnNames))
columnPointers := make([]interface{}, len(columnNames))
for i := 0; i < len(columnNames); i++ {
columnPointers[i] = &columns[i]
}
if err := rows.Scan(columnPointers...); err != nil {
log.Fatalln(err)
}
现在columns
切片应该包含当前结果行的所有列值的解码版本。
如果你对表格有额外的了解(例如预期的类型,或者提前了解列数),你可以稍微简化逻辑。
答案 1 :(得分:7)
找到go-mssqldb驱动程序的示例代码。参考。 https://github.com/denisenkom/go-mssqldb/blob/master/examples/tsql/tsql.go - 下面提取的代码。 它至少适用于这个驱动程序。但是,它只使用sql-namespace API,因此它可能/可能也适用于其他驱动程序。
给定任何(?)SQL select语句,它显示结果数据行
func exec(db *sql.DB, cmd string) error {
rows, err := db.Query(cmd)
if err != nil {
return err
}
defer rows.Close()
cols, err := rows.Columns()
if err != nil {
return err
}
if cols == nil {
return nil
}
vals := make([]interface{}, len(cols))
for i := 0; i < len(cols); i++ {
vals[i] = new(interface{})
if i != 0 {
fmt.Print("\t")
}
fmt.Print(cols[i])
}
fmt.Println()
for rows.Next() {
err = rows.Scan(vals...)
if err != nil {
fmt.Println(err)
continue
}
for i := 0; i < len(vals); i++ {
if i != 0 {
fmt.Print("\t")
}
printValue(vals[i].(*interface{}))
}
fmt.Println()
}
if rows.Err() != nil {
return rows.Err()
}
return nil
}
func printValue(pval *interface{}) {
switch v := (*pval).(type) {
case nil:
fmt.Print("NULL")
case bool:
if v {
fmt.Print("1")
} else {
fmt.Print("0")
}
case []byte:
fmt.Print(string(v))
case time.Time:
fmt.Print(v.Format("2006-01-02 15:04:05.999"))
default:
fmt.Print(v)
}
}