我正在编写一个基本程序来读取数据库表中的值并在表中打印。该表由一个古老的程序填充。行中的某些字段是可选的,当我尝试将它们作为字符串读取时,我收到以下错误:
panic: sql: Scan error on column index 2: unsupported driver -> Scan pair: <nil> -> *string
在我阅读了类似问题的其他问题之后,我想出了以下代码来处理nil值。该方法在实践中工作正常。我得到纯文本和空字符串的值而不是nil值。
但是,我有两个问题:
我做错了吗?是否有更好/更清洁/更有效/惯用的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
}
答案 0 :(得分:20)
有多种类型可以处理来自数据库的null
值,例如sql.NullBool
,sql.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
}
答案 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...
}
注意用于强制转换为特定值的漂亮行方法。空值由库处理,规则记录在此处: