当我在 db(这里是 postgres)中的结构未知时,如何在 GoLang 数据库/sql 中检索值?



我使用Go和PostgreSQL使用 github.com/lib/pq 并且能够在知道我的结构时成功获取记录。 现在我的查询是当我的结构动态变化时如何获取记录?

通过rows.columns我可以获取列名,但您能否帮助我获取所有行的这些列的值。我提到了@Luke回答的这个链接,尽管如此,这里的人已经定义了一个结构。 是否可以使用 GoLang 数据库/sql 按名称检索列值

type Person struct {
Id int
Name string
}

同时我没有固定的结构,所以我将如何再次遍历所有行的所有列。我的方法是首先是一个指针来遍历所有列,然后另一个指针用于转到下一行。 仍然无法对此进行编码,您能否帮助我解决这个问题,例如如何继续并获取值。

由于您不知道前面的结构,因此可以将行作为空接口的二维切片返回。但是,要使行扫描正常工作,您需要将值预先分配给适当的类型,为此,您可以使用ColumnTypes方法和reflect包。请记住,并非每个驱动程序都提供对列类型的访问,因此请确保您使用的驱动程序提供对列类型的访问。

rows, err := db.Query("select * from foobar")
if err != nil {
return err
}
defer rows.Close()
// get column type info
columnTypes, err := rows.ColumnTypes()
if err != nil {
return err
}
// used for allocation & dereferencing
rowValues := make([]reflect.Value, len(columnTypes))
for i := 0; i < len(columnTypes); i++ {
// allocate reflect.Value representing a **T value
rowValues[i] = reflect.New(reflect.PtrTo(columnTypes[i].ScanType()))
}
resultList := [][]interface{}{}
for rows.Next() {
// initially will hold pointers for Scan, after scanning the
// pointers will be dereferenced so that the slice holds actual values
rowResult := make([]interface{}, len(columnTypes))
for i := 0; i < len(columnTypes); i++ {
// get the **T value from the reflect.Value
rowResult[i] = rowValues[i].Interface()
}
// scan each column value into the corresponding **T value
if err := rows.Scan(rowResult...); err != nil {
return err
}
// dereference pointers
for i := 0; i < len(rowValues); i++ {
// first pointer deref to get reflect.Value representing a *T value,
// if rv.IsNil it means column value was NULL
if rv := rowValues[i].Elem(); rv.IsNil() {
rowResult[i] = nil
} else {
// second deref to get reflect.Value representing the T value
// and call Interface to get T value from the reflect.Value
rowResult[i] = rv.Elem().Interface()
}
}
resultList = append(resultList, rowResult)
}
if err := rows.Err(); err != nil {
return err
}
fmt.Println(resultList)

此函数打印查询结果时不知道列类型和计数。它是不使用reflect包的先前答案的变体。

func printQueryResult(db *sql.DB, query string) error {
rows, err := db.Query(query)
if err != nil {
return fmt.Errorf("canot run query %s: %w", query, err)
}
defer rows.Close()
cols, _ := rows.Columns()
row := make([]interface{}, len(cols))
rowPtr := make([]interface{}, len(cols))
for i := range row {
rowPtr[i] = &row[i]
}
fmt.Println(cols)
for rows.Next() {
err = rows.Scan(rowPtr...)
if err != nil {
fmt.Println("cannot scan row:", err)
}
fmt.Println(row...)
}
return rows.Err()
}

诀窍是rows.Scan可以将值扫描到*interface{}中,但您必须将其包装在interface{}中才能使用...将其传递给Scan

最新更新