Link Search Menu Expand Document

go进行sql查询遇到NULL字段的解决方法

mysql数据库查询数据时,经常出现查询的某个字段为NULL的情况。

然而这种情况在go语言中会出现一种很蛋疼的表现:go在Scan结果的时候,会失败。

案例

  • 数据表是这样的
CREATE TABLE `go_test`.`test_nulltest_null`( 
   `id` INT NOT NULL AUTO_INCREMENT , 
   `name` VARCHAR(200) , 
   `info` VARCHAR(500) , 
   PRIMARY KEY (`id`)
 );
  • 里面的字段是这样的
INSERT INTO `go_test`.`test_null`(`id`,`name`,`info`) VALUES ( NULL,'123',NULL);
  • 读取内容的代码是这样的
func queryDb(db *sql.DB) {
	rows, err := db.Query("select * from go_test.test_null")
	if err != nil {
		fmt.Println("查询出错:" + err.Error())
	}
	defer rows.Close()

	for
	rows.Next() {
		var id int
		var name, info string
		if err = rows.Scan(&id, &name, &info); err != nil {
			fmt.Println("获取结果出错:" + err.Error())
			return
		}
		fmt.Println("id:", id, "name:", name, "info:", info)
	}
}
  • 结果是这样的
获取结果出错:sql: Scan error on column index 2: unsupported Scan, storing driver.Value type <nil> into type *string 

很明显,这不是我们想要的结果,如果这个值木有,scan的时候给我们个空字符串也好啊,然而结果是报错了。下面研究一下解决方案。

一种可行的解决方案

对于可能为NULL的字段读取,go的sql模块提供了一个sql.NullString这样的东东来解析可能为NULL的字符串,对于浮点型或者整形也有类似的数据类型。很蛋疼的样子,不过可以解决问题,下面是遵照这种方案进行的修改。

  • 代码进行如下修改
rows.Next() {
  var id int
  var name, info sql.NullString
  if err = rows.Scan(&id, &name, &info); err != nil {
    fmt.Println("获取结果出错:" + err.Error())
    return
  }
  fmt.Println("id:", id, "name:", name.String, "info:", info.String)
}
  • 正常的结果如下
id: 1 name: 123 info: 

按照这种解决方法,每次写go代码的时候,还得去顾及sql查询结果是否有NULL出现,肿么看都是很蛋疼的事情。好在sql语句有自己的解决方法,请继续向下看。

更好的解决方案

如果思考go语言的时候,只需要思考go语言,sql的问题,在思考sql的时候解决是更好的方案。今天研究了一下mysql,发现sql语句本身也能解决这种问题。

  • 优化的sql查询语句
SELECT id, IFNULL(`name`, '') AS `name`, IFNULL(info, '') AS info FROM go_test.test_null

说明一下,增加的IFNULL判断,是在发现该字段为NULL的时候,返回另外一个值(此处用空字符串替代)。

  • 现在把scan处的代码恢复原状,只是把sql语句修改一下,一切也都是OK的了
  • 修改后完整的代码如下
func queryDb(db *sql.DB) {
	rows, err := db.Query("SELECT id, IFNULL(`name`, '') AS `name`, IFNULL(info, '') AS info FROM go_test.test_null")
	if err != nil {
		fmt.Println("查询出错:" + err.Error())
	}
	defer rows.Close()

	for
	rows.Next() {
		var id int
		var name, info string
		if err = rows.Scan(&id, &name, &info); err != nil {
			fmt.Println("获取结果出错:" + err.Error())
			return
		}
		fmt.Println("id:", id, "name:", name, "info:", info)
	}
}

总结陈词

如果以后使用go进行sql查询的时候,为了避免在go中进行一些蛋疼的对NULL值的额外处理,可以考虑用sql的IFNULL语句来将可能的NULL值进行转化。


千里之行,始于足下。