最近在将工作中写的一些python和ruby小脚本用Go重写一遍,打包分发给组员,提高大家的工作效率,Go开发的工具可以直接打包成exe,分发起来更加的快捷,把文件解压到一个目录即可使用,极大的降低了使用门槛。
而在工具中有个典型的需求,就是根据条件查询数据库记录,然后在命令行里以key:value的形式打印出来,对于python,ruby这样的语言实现起来很容易,只需要三步:
1. 用查询语句查询记录:如: select * from xxx where xxx
2. 使用fetchrow函数获取记录,一般是一个hashmap 或者python的tuple
3. 使用标准库pprint打印这个数据到屏幕
当我想要在Go实现同样的功能的时候,在第二步遇到了麻烦,查询了网上的Go查询mysql数据的代码,典型做法是把查询的所有字段都写出来,或用变量,或者封装在一个结构体里,然后再Scan函数里按顺序写入。示例代码如下:
type Data struct{
a string
b string
...
}
func getData() *Data{
...
for rows.Next() {
data:=&Data{}
rows.Scan(&Data.a,&Data.b,...)
}
...
}
对于一个有20多个字段的表,做简单的查看操作,非常不优雅。结合网上的资料和自己的实践,我写出一套代码,实现了如下目标:
1. 将查询结果返回成 map[string]string ,支持根据columnname查询字段结果
2. 使用[]string记录了column顺序,遍历可以让数据的顺序和数据库列名的顺序保持一致
3. 使用 sql.NullString ,当查询出来的数据记录中包含null值,自动转为空字符串,不会影响后续列的结果。
代码如下:
type Manage struct {
base map[string]string //记录返回结果
keys []string //记录列名顺序
}
func makeEmptyStr() *sql.NullString {
var s sql.NullString
return &s
}
func main(){
driver := "root:abcde@tcp(localhost:3306)/db3?charset=utf8"
db, err := sql.Open("mysql", driver)
if err != nil {
fmt.Println(err)
return
}
defer db.Close()
sqls := "select * from orders "
rows, err := db.Query(sqls)
if err != nil {
fmt.Println(err)
return
}
defer rows.Close()
columns, err := rows.Columns()
if err != nil {
fmt.Println(err)
return
}
manages:=[]*Manage{}
for rows.Next() {
keys := make([]string, 0, len(columns))
resultmap := make(map[string]string, len(columns))
rowdatas := make([]interface{}, len(columns))
for i := 0; i < len(rowdatas); i++ {
rowdatas[i] = makeEmptyStr()
}
rows.Scan(rowdatas...)
for i := 0; i < len(rowdatas); i++ {
k := columns[i]
val := (rowdatas[i].(*sql.NullString))
v := ""
if val.Valid {
v = val.String
}
resultmap[k] = v
keys = append(keys, k)
}
manages=append(manages,&Manage{base: resultmap, keys: keys})
}
manage:=manages[0]
for i := 0; i < len(manage.keys); i++ {
k := manage.keys[i]
v := manage.base[k]
fmt.Println(k + ": " + v)
}
}