Golang的SQL包是否无法进行临时/探索性查询?

时间:2014-05-07 02:08:14

标签: sql database go

根据文档,似乎在Go中从数据库中获取数据的唯一方法是使用Rows.Scan(),这意味着您必须在编译时知道所有列的计数和类型。

我错过了什么吗?你是如何支持即席查询的?或者甚至将所有列从表中拉出来,将来可能会发生变化?

2 个答案:

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