go语言scan空值报错

来源:互联网 发布:UC数据库设计 编辑:程序博客网 时间:2024/05/18 02:23

http://xmarker.blog.163.com/blog/static/22648405720144743531420/


今天使用go语言时,如果数据库里有空值,扫描出来报错,其中环境是这样的,数据库为postgresql9.3.2(其实和数据库无关),数据库里的数据如下:

db_public=> \d t_tmp
             Table "usr_public.t_tmp"
 Column |            Type             | Modifiers 
--------+-----------------------------+-----------
 id     | integer                     | not null
 name   | character varying(30)       | 
 remark | text                        | 
 time   | timestamp without time zone | 
 age    | numeric(18,4)               | 
Indexes:
    "t_tmp_pkey" PRIMARY KEY, btree (id)

db_public=> select * from t_tmp;
 id |  name  |   remark    |            time            |   age   
----+--------+-------------+----------------------------+---------
  1 | zyj071 | insert test | 2014-04-02 17:17:21.176196 |        
  2 | 马到成功 |             | 2014-05-07 15:23:27.479841 |        
  3 | 众人  | 测试        | 2014-05-07 15:38:14.135305 | 17.2340
(3 rows)

go代码如下:
 package main
import (
    "fmt"
    "time"
    "zypublic/zyutil/zydbhelp"
)
func main() {
    conn, err := zydbhelp.ConnDBByService("public", true)
    if err != nil {
        fmt.Println(err)
    }
    stmt, err := conn.Prepare("select id,name,remark,time,age from t_tmp")
    if err != nil {
        fmt.Println(err)
    }
    var vid int64
    var vname string
    var vremark string
    var vtime time.Time
    var vage float64
    rows, err := stmt.Query()
    if err != nil {
        fmt.Println(err)
    }
    for rows.Next() {
        err = rows.Scan(&vid, &vname, &vremark, &vtime, &vage)
        if err != nil {
            fmt.Println("scan报错:", err)
        }
        fmt.Println("result是:", vid, "<==>", vname, "<==>", vremark, "<==>", vtime, "<==>", vage)
    }
}
执行结果如下:

scan报错: sql: Scan error on column index 4: converting string "<nil>" to a float64: strconv.ParseFloat: parsing "<nil>": invalid syntax

result是: 1 <==> zyj071 <==> insert test <==> 2014-04-02 17:17:21.176196 +0000 UTC <==> 0

scan报错: sql: Scan error on column index 2: unsupported driver -> Scan pair: <nil> -> *string

result是: 2 <==> 马到成功<==> insert test <==> 2014-04-02 17:17:21.176196 +0000 UTC <==> 0

result是: 3 <==> 众人 <==> 测试 <==> 2014-05-07 15:38:14.135305 +0000 UTC <==> 17.234


   经过一番google后发现,go语言有自带的sql.NullFloat类型可供我们使用,于是用sql.NullFloat64来代替Float64,代码如下:

var vid int64
    var vname string
    var vremark sql.NullString
    var vtime time.Time
    var vage sql.NullFloat64
然后再编译运行,结果如下:

result是: 1 <==> zyj071 <==> {insert test true} <==> 2014-04-02 17:17:21.176196 +0000 UTC <==> {0 false}

result是: 2 <==> 马到成功 <==> { false} <==> 2014-05-07 15:23:27.479841 +0000 UTC <==> {0 false}

result是: 3 <==> 众人 <==> {测试 true} <==> 2014-05-07 15:38:14.135305 +0000 UTC <==> {17.234 true}

可以看到vremark和vage都是struct类型来,经过查看godoc,sql.Null类型的为struct结构体:

type NullFloat64 struct {    Float64 float64    Valid   bool // Valid is true if Float64 is not NULL}

后续处理可以判断vage.Valid是否为true写个if else进行处理。


本来以为到这就结束了,但之前我不经意间查询都是没问题的,为啥这次有问题呢,仔细核对代码,发现这部分有问题:

err = rows.Scan(&vid, &vname, &vremark, &vtime, &vage)

        if err != nil {

            fmt.Println("scan报错:", err)

        }
如果不用err接收rows.Scan的结果,数据都是可以正常输出的:
rows.Scan(&vid, &vname, &vremark, &vtime, &vage)
这样显示如下:

result是: 1 <==> zyj071 <==> insert test <==> 2014-04-02 17:17:21.176196 +0000 UTC <==> 0

result是: 2 <==> 马到成功 <==> insert test <==> 2014-04-02 17:17:21.176196 +0000 UTC <==> 0

result是: 3 <==> 众人 <==> 测试 <==> 2014-05-07 15:38:14.135305 +0000 UTC <==> 17.234

这样不需要使用sql.Null***类型,用原始类型就可以处理了,但这样的话null会显示成0或空字符串,而且第二行马到成功后面在数据库是空值,但查询出来竟然也是insert test,使用的是上一行的值,因此一定不能忘记用err接收rows.Scan的结果!


0 0