Heroku 是否可以将golang db.Query()输出转储为字符串?

Heroku 是否可以将golang db.Query()输出转储为字符串?,heroku,go,Heroku,Go,我有一个小的Heroku应用程序,在其中我在查询执行后打印出每行的姓名和年龄 我想避免循环行。下一步(),扫描()。。并且只想显示查询执行后返回的数据库,可能是一些数据或错误 我们可以直接将数据转储到字符串中进行打印吗 rows, err := db.Query("SELECT name FROM users WHERE age = $1", age) if err != nil { log.Fatal(err) } for rows.Next() { var name str

我有一个小的Heroku应用程序,在其中我在查询执行后打印出每行的姓名和年龄

我想避免循环行。下一步(),扫描()。。并且只想显示查询执行后返回的数据库,可能是一些数据或错误

我们可以直接将数据转储到字符串中进行打印吗

rows, err := db.Query("SELECT name FROM users WHERE age = $1", age)

if err != nil {
    log.Fatal(err)
}
for rows.Next() {
    var name string
    if err := rows.Scan(&name); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
    log.Fatal(err)
}
差不多:没有

查询方法将返回一个指向Rows结构的指针:

func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
如果打印(
fmt.Printf(“%#v\n”,行)
),您将看到如下内容:

&sql.Rows{dc:(*sql.driverConn)(0xc8201225a0),releaseConn:(func(error)(0x4802c0),rowsi:(*pq.Rows)(0xc820166700),closed:false,lastcols:[]驱动程序.Value(nil),lasterr:error(nil),closeStmt:driver.Stmt(nil)}

…可能不是你想要的

这些对应于中的Rows结构(您会注意到字段未导出):

您将看到
[]driver.Value
(来自驱动程序包的接口),这看起来像是我们可以期望找到一些有用的,甚至是人类可读的数据的地方。但是,当直接打印时,它看起来没有用处,甚至是空的……因此,您必须以某种方式获取底层信息。sql包为我们提供了下一个开始的方法:

下一步准备下一个结果行,以便使用扫描方法读取。 成功时返回true,如果没有下一个,则返回false 结果行或准备时出错。错误 应咨询以区分这两种情况

每次调用Scan(即使是第一次)之前都必须调用Next

接下来将生成一个[]driver.Value,其大小与我拥有的列数相同,可以通过driver.Rows(rowsi字段)访问它(在sql包中),并用查询中的值填充它

调用rows.Next()后,如果您使用相同的
fmt.Printf(“%#v\n”,rows)
您现在应该看到[]diver.Value不再为空,但它仍然不是您可以读取的任何内容,更可能类似于:
[]diver.Value{[]uint8{0x47,0x65…

由于字段没有导出,您甚至无法尝试将其转换为更有意义的内容。但是sql包为我们提供了一种处理数据的方法,即扫描

Scan方法非常简洁,我不会在这里粘贴冗长的注释,但真正重要的一点是,它覆盖从下一个方法获得的当前行中的列,并调用
convertsign(dest[I],sv)
,您可以在这里看到:

它相当长,但实际上相对简单,它基本上打开源和目标的类型,并在可以转换的地方进行转换,并从源复制到目标;函数注释告诉我们:

convertAssign copies to dest将src中的值分配给dest,如果可能,将其转换。如果复制会导致信息丢失,则返回错误。dest应为指针类型

现在您有了一个方法(Scan),您可以直接调用该方法,并将转换后的值返回给您

认识到sql包必须与特定的驱动程序一起工作是很重要的,而驱动程序又是为特定的数据库软件实现的,因此在幕后有相当多的工作要做

如果要隐藏/概括整个查询()-->Next()-->Scan(),我认为这是最好的选择习惯用法是将其放入另一个在幕后执行的函数中……编写一个包,在其中抽象掉更高级别的实现,因为sql包抽象掉一些特定于驱动程序的细节、转换和复制、填充行等

type Rows struct {
    dc          *driverConn // owned; must call releaseConn when closed to release
    releaseConn func(error)
    rowsi       driver.Rows

    closed    bool
    lastcols  []driver.Value
    lasterr   error       // non-nil only if closed is true
    closeStmt driver.Stmt // if non-nil, statement to Close on close
    }