Golang实践录:查询数据表的几种方式

Posted 李迟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang实践录:查询数据表的几种方式相关的知识,希望对你有一定的参考价值。

本文汇总一些工程中使用到的查询数据表的代码示例。由于是代码片段,不一定保证完整。但其思想可以参考。

问题提出

工程中经常要使用到数据表(工作多年,不可避免沦为了CRUD工具人),打交道最多的是查询,对于插入更新接触比较少,由于所涉及的数据表是生产环境的,所以不敢越雷池半步。

golangsql包虽然没有包含数据库驱动,但在语言级别统一了接口,所以使用上还是有一定的便利之处。之前用golang写的一个内部工具,需要使用到mysql、oracle、sqlite3三种不同的数据库,就是看上golang的这个特点。
然而有时数据表字段实在太多了,而很多表的处理方法是一样的,如果均一一编写,则略显繁琐。本文综合一些资料,根据经验,汇总了几种查询数据表的方式。

解决方案

方案一:指定字段变量

func GetIntervalDetail_inner(sqldb *sql.DB, version string) (rets []conf.TestInfo) 
	sqlstr := fmt.Sprintf("select * from TestTable a where a.version = '%v' order by a.ID", version)
	// klog.Println("sqlstr: ", sqlstr)
	results, err := sqldb.Query(sqlstr)
	if err != nil 
		klog.Printf("Query error: %s\\n", err.Error())
		return
	

	for results.Next() 
		var item1, item2, item3, item4, item5, item7, item9, item10 sql.NullString
		var item6, item8 int

		err := results.Scan(&item1, &item2, &item3, &item4, &item5,
			&item6, &item7, &item8, &item9, &item10)
		if err != nil 
			klog.Println("scan error: ", err)
			break
		
		// item7 有空格,先去掉
		item7.String = strings.TrimSpace(item7.String)
		if item7.String == "" 
			item7.String = "none"
		

		var tmp conf.TestInfo
		tmp.Version = item1.String
		tmp.Code = item2.String
		tmp.ID = item3.String
		tmp.Code1 = item4.String
		tmp.Code2 = item5.String
		tmp.Length = item6
		tmp.F1_CODE = item7.String
		tmp.F1_length = item8
		tmp.F2_CODE = item9.String
		tmp.F2_length = item10.String

		rets = append(rets, tmp)
	

	return


即使用到结构体,其本质也没有发生变化

func GetSpecialFee_inner(sqldb *sql.DB, Version string) (rets []conf.MySpeedRate) 
	sqlstr := fmt.Sprintf("select * from FoobarTable where Version=%v", Version)
	// klog.Println("sqlstr: ", sqlstr)
	results, err := sqldb.Query(sqlstr)
	if err != nil 
		klog.Printf("Query error: %s\\n", err.Error())
		return
	

	for results.Next() 
		var data conf.MySpeedRate
		err := results.Scan(&data.Version, &data.Rate[0], &data.Rate[1],
			&data.Rate[2], &data.Rate[3], &data.Rate[4], &data.Rate[5], &data.Rate[6], &data.Rate[7],
			&data.Rate[8], &data.Rate[9], &data.Rate[10], &data.Rate[11], &data.Rate[12], &data.Rate[13],
			&data.Rate[14], &data.Rate[15])
		if err != nil 
			klog.Println("scan error: ", err)
			break
		
		rets = append(rets, data)
	

	return

方案二:泛化:结构体

func GetAllRowsPtr(sqldb *sql.DB, query string, strname interface) (*[]interface, error) 
	result := make([]interface, 0)

	rows, err := sqldb.Query(query)
	if err != nil 
		return &result, err
	
	defer rows.Close()

	s := reflect.ValueOf(strname).Elem()
	leng := s.NumField()
	scans := make([]interface, leng)
	for i := 0; i < leng; i++ 
		scans[i] = s.Field(i).Addr().Interface()
	
	for rows.Next() 
		err = rows.Scan(scans...)
		if err != nil 
			panic(err)
		
		result = append(result, s.Interface())
	

	return &result, nil

方案三:泛化:字符串数组

func GetAllRowsString(sqldb *sql.DB, query string) ([]string, error) 
	rows, err := sqldb.Query(query)
	if err != nil 
		return []string, err
	
	defer rows.Close()

	var results []string
	var tmp string

	// 获取字段名称
	tmp = ""
	cols, _ := rows.Columns()
	for i := range cols 
		tmp += cols[i] + ","
	
	results = append(results, tmp)

	// 根据字段数量,指定查询scan的参数
	values := make([]sql.RawBytes, len(cols))
	scans := make([]interface, len(cols))
	for i := range values 
		scans[i] = &values[i]
	

	for rows.Next() 
		if err := rows.Scan(scans...); err != nil 
			fmt.Println("Error")
			return []string, err
		
		// 组装,暂定以逗号隔开
		tmp = ""
		for j := range values 
			tmp += string(values[j]) + ","
		
		results = append(results, tmp)
	

	return results, nil

小结

方案一是将数据表映射到结构体中,因此需要定义对应的结构体,但可以与数据表不一一对应,即根据sql语句只查询个别字段并赋值,如果不嫌麻烦,或者结构体随处需要使用,则可用此法。

方案二不需要定义结构体,实际使用的是接口,使用时需指定字段名称。实现代码中,如果查询的数据表字段和总字段不匹配,会提示panic: sql: expected 19 destination arguments in Scan, not 4

方案三在方案二基础上进行修改。会自动匹配查询sql的字段。最终组装成csv格式字符串数组。

不同方案均有优劣,按需使用即可。

以上是关于Golang实践录:查询数据表的几种方式的主要内容,如果未能解决你的问题,请参考以下文章

Golang实践录:map的几个使用示例

Golang实践录:map的几个使用示例

Golang实践录:map的几个使用示例

Golang实践录:获取系统信息

Golang实践录:获取系统信息

Golang实践录:调用C++函数的优化