douyi8315 2016-04-19 11:12
浏览 49
已采纳

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

I have a small Heroku app in which i print out name and age from each rows after query execution.

I want to avoid looping rows.Next(),Scan().. and just want to show what database returned after query execution which may be some data or error.

Can we directly dump data to a string for printing?

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
", name, age)
}
if err := rows.Err(); err != nil {
    log.Fatal(err)
}
  • 写回答

1条回答 默认 最新

  • doutao4480 2016-04-19 15:03
    关注

    Pretty much: No.

    The Query method is going to return a pointer to a Rows struct:

    func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
    

    If you print that (fmt.Printf("%#v ", rows)) you'll see something such as:

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

    ...probably not what you want.

    Those correspond to the Rows struct from the sql package (you'll notice the fields are not exported):

    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
        }
    

    You'll see []driver.Value (an interface from the driver package), that looks like where we can expect to find some useful, maybe even human readable data. But when directly printed it doesn't appear useful, it's even empty... So you have to somehow get at the underlying information. The sql package gives us the Next method to start with:

    Next prepares the next result row for reading with the Scan method. It returns true on success, or false if there is no next result row or an error happened while preparing it. Err should be consulted to distinguish between the two cases.

    Every call to Scan, even the first one, must be preceded by a call to Next.

    Next is going to make a []driver.Value the same size as the number of columns I have, which is accessible (within the sql package) through driver.Rows (the rowsi field) and populate it with values from the query.

    After calling rows.Next() if you did the same fmt.Printf("%#v ", rows) you should now see that []diver.Value is no longer empty but it's still not going to be anything that you can read, more likely something resembling:[]diver.Value{[]uint8{0x47, 0x65...

    And since the field isn't exported you can't even try and convert it to something more meaningful. But the sql package gives us a means to do something with the data, which is Scan.

    The Scan method is pretty concise, with lengthy comments that I won't paste here, but the really important bit is that it ranges over the columns in the current row you get from the Next method and calls convertAssign(dest[i], sv), which you can see here: https://golang.org/src/database/sql/convert.go

    It's pretty long but actually relatively simple, it essentially switches on the type of the source and destination and converts where it can, and copies from source to destination; the function comments tell us:

    convertAssign copies to dest the value in src, converting it if possible. An error is returned if the copy would result in loss of information. dest should be a pointer type.

    So now you have a method (Scan) which you can call directly and which hands you back converted values. Your code sample above is fine (except maybe the call to Fatal() on a Scan error).

    It's important to realize that the sql package has to work with a specific driver, which is in turn implemented for specific database software, so there is quite some work going on behind the scenes.

    I think your best bet if you want to hide/generalize the whole Query() ---> Next() ---> Scan() idiom is to drop it into another function which does it behind the scenes... write a package in which you abstract away that higher level implementation, as the sql package abstracts away some of the driver-specific details, the converting and copying, populating the Rows, etc.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效
  • ¥15 悬赏!微信开发者工具报错,求帮改
  • ¥20 wireshark抓不到vlan
  • ¥20 关于#stm32#的问题:需要指导自动酸碱滴定仪的原理图程序代码及仿真
  • ¥20 设计一款异域新娘的视频相亲软件需要哪些技术支持
  • ¥15 stata安慰剂检验作图但是真实值不出现在图上
  • ¥15 c程序不知道为什么得不到结果
  • ¥40 复杂的限制性的商函数处理
  • ¥15 程序不包含适用于入口点的静态Main方法