如何处理数据库中的nil返回值?

时间:2015-02-21 06:20:56

标签: database go

我正在编写一个基本程序来读取数据库表中的值并在表中打印。该表由一个古老的程序填充。行中的某些字段是可选的,当我尝试将它们作为字符串读取时,我收到以下错误:

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

在我阅读了类似问题的其他问题之后,我想出了以下代码来处理nil值。该方法在实践中工作正常。我得到纯文本和空字符串的值而不是nil值。

但是,我有两个问题:

  1. 这看起来效率不高。我需要处理25个这样的字段,这意味着我将每个字段作为字节读取并转换为字符串。函数调用和转换太多。处理数据的两个结构等等......
  2. 代码看起来很难看。它已经看起来令人费解了2个字段,并且在我去25 +
  3. 时变得不可读

    我做错了吗?是否有更好/更清洁/更有效/惯用的golang方式从数据库中读取值?

    我发现很难相信像Go这样的现代语言不会优雅地处理数据库。

    提前致谢!

    代码段:

    // DB read format
    type udInfoBytes struct {
      id                     []byte
      state                  []byte
    }
    
    // output format
    type udInfo struct {
      id                     string
      state                  string
    }
    
    func CToGoString(c []byte) string {
      n := -1
      for i, b := range c {
        if b == 0 {
          break
        }
        n = i
      }
      return string(c[:n+1])
    }
    
    func dbBytesToString(in udInfoBytes) udInfo {
    
      var out udInfo
      var s string
      var t int
    
      out.id = CToGoString(in.id)
      out.state = stateName(in.state)
      return out
    }
    
    func GetInfo(ud string) udInfo {
    
      db := getFileHandle()
      q := fmt.Sprintf("SELECT id,state FROM Mytable WHERE id='%s' ", ud)
    
      rows, err := db.Query(q)
      if err != nil {
        log.Fatal(err)
      }
      defer rows.Close()
      ret := udInfo{}
      r := udInfoBytes{}
      for rows.Next() {
        err := rows.Scan(&r.id, &r.state)
    
        if err != nil {
          log.Println(err)
        }
        break
      }
      err = rows.Err()
      if err != nil {
        log.Fatal(err)
      }
    
      ret = dbBytesToString(r)
      defer db.Close()
      return ret
    }
    

    修改

    我希望有类似下面的内容,我不必担心处理NULL并自动将它们作为空字符串读取。

    // output format
    type udInfo struct {
      id                     string
      state                  string
    }
    
    func GetInfo(ud string) udInfo {
    
      db := getFileHandle()
      q := fmt.Sprintf("SELECT id,state FROM Mytable WHERE id='%s' ", ud)
    
      rows, err := db.Query(q)
      if err != nil {
        log.Fatal(err)
      }
      defer rows.Close()
      r := udInfo{}
    
      for rows.Next() {
        err := rows.Scan(&r.id, &r.state)
    
        if err != nil {
          log.Println(err)
        }
        break
      }
      err = rows.Err()
      if err != nil {
        log.Fatal(err)
      }
    
      defer db.Close()
      return r
    }
    

5 个答案:

答案 0 :(得分:20)

有多种类型可以处理来自数据库的null值,例如sql.NullBoolsql.NullFloat64等。

例如:

 var s sql.NullString
 err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
 ...
 if s.Valid {
    // use s.String
 } else {
    // NULL value
 }

答案 1 :(得分:8)

go的数据库/ sql包处理类型的指针。

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/mattn/go-sqlite3"
    "log"
)

func main() {
    db, err := sql.Open("sqlite3", ":memory:")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    _, err = db.Exec("create table foo(id integer primary key, value text)")
    if err != nil {
        log.Fatal(err)
    }
    _, err = db.Exec("insert into foo(value) values(null)")
    if err != nil {
        log.Fatal(err)
    }
    _, err = db.Exec("insert into foo(value) values('bar')")
    if err != nil {
        log.Fatal(err)
    }
    rows, err := db.Query("select id, value from foo")
    if err != nil {
        log.Fatal(err)
    }
    for rows.Next() {
        var id int
        var value *string
        err = rows.Scan(&id, &value)
        if err != nil {
            log.Fatal(err)
        }
        if value != nil {
            fmt.Println(id, *value)
        } else {
            fmt.Println(id, value)
        }
    }
}

你应该如下:

1 <nil>
2 bar

答案 2 :(得分:1)

另一种解决方案是使用COALESCE函数在SQL语句本身中处理这个问题(虽然并非所有数据库都支持这个)。

例如,您可以使用:

q := fmt.Sprintf("SELECT id,COALESCE(state, '') as state FROM Mytable WHERE id='%s' ", ud)

如果在数据库中将其作为NULL存储,则有效地为'state'提供空字符串的默认值。

答案 3 :(得分:0)

两种处理这些空值的方法:

使用sql.NullString

if value.Valid { 
   return value.String
} 

使用* string

if value != nil {
   return *value
}

https://medium.com/@raymondhartoyo/one-simple-way-to-handle-null-database-value-in-golang-86437ec75089

答案 4 :(得分:-2)

我已经开始使用MyMySql驱动程序,因为它使用了比std库更好的接口。

https://github.com/ziutek/mymysql

然后我将查询数据库包装成简单易用的函数。这是一个这样的功能:

import "github.com/ziutek/mymysql/mysql"
import _ "github.com/ziutek/mymysql/native"

// Execute a prepared statement expecting multiple results.
func Query(sql string, params ...interface{}) (rows []mysql.Row, err error) {
    statement, err := db.Prepare(sql)
    if err != nil {
        return
    }
    result, err := statement.Run(params...)
    if err != nil {
        return
    }
    rows, err = result.GetRows()
    return
}

使用它就像这个片段一样简单:

rows, err := Query("SELECT * FROM table WHERE column = ?", param)

for _, row := range rows {
    column1 = row.Str(0)
    column2 = row.Int(1)
    column3 = row.Bool(2)
    column4 = row.Date(3)
    // etc...
}

注意用于强制转换为特定值的漂亮行方法。空值由库处理,规则记录在此处:

https://github.com/ziutek/mymysql/blob/master/mysql/row.go

相关问题