美文网首页
golang操作mysql聚合结果

golang操作mysql聚合结果

作者: 杜子龙 | 来源:发表于2020-08-09 18:29 被阅读0次

这里是基于github.com/gocraft/dbr三方库的改进,该三方库不支持不支持对mysql查询结果聚合。
原始方法如下:

// Load loads any value from sql.Rows
func Load(rows *sql.Rows, value interface{}) (int, error) {
    defer rows.Close()

    column, err := rows.Columns()
    if err != nil {
        return 0, err
    }

    v := reflect.ValueOf(value)
    if v.Kind() != reflect.Ptr || v.IsNil() {
        return 0, ErrInvalidPointer
    }
    v = v.Elem()
    isSlice := v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8
    count := 0
    for rows.Next() {
        var elem reflect.Value
        if isSlice {
            elem = reflect.New(v.Type().Elem()).Elem()
        } else {
            elem = v
        }
        ptr, err := findPtr(column, elem)
        if err != nil {
            return 0, err
        }
        err = rows.Scan(ptr...)
        if err != nil {
            return 0, err
        }
        count++
        if isSlice {
            v.Set(reflect.Append(v, elem))
        } else {
            break
        }
    }
    return count, nil
}

type dummyScanner struct{}

func (dummyScanner) Scan(interface{}) error {
    return nil
}

var (
    dummyDest   sql.Scanner = dummyScanner{}
    typeScanner             = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
)

func findPtr(column []string, value reflect.Value) ([]interface{}, error) {
    if value.Addr().Type().Implements(typeScanner) {
        return []interface{}{value.Addr().Interface()}, nil
    }
    switch value.Kind() {
    case reflect.Struct:
        var ptr []interface{}
        m := structMap(value)
        for _, key := range column {
            if val, ok := m[key]; ok {
                ptr = append(ptr, val.Addr().Interface())
            } else {
                ptr = append(ptr, dummyDest)
            }
        }
        return ptr, nil
    case reflect.Ptr:
        if value.IsNil() {
            value.Set(reflect.New(value.Type().Elem()))
        }
        return findPtr(column, value.Elem())
    }
    return []interface{}{value.Addr().Interface()}, nil
}

改进方法如下:

//当存在聚合结果的时候调用api 如果没有聚合 建议使用dbr的load接口
func Load(rows *sql.Rows, value interface{}) (int, error) {
    defer rows.Close()
    columns, err := rows.Columns()
    if err != nil {
        return 0, err
    }

    v := reflect.ValueOf(value)
    if v.Kind() != reflect.Ptr || v.IsNil() {
        return 0, dbr.ErrInvalidPointer
    }
    v = v.Elem()
    isSlice := v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8
    //if !isSlice {
    //  return 0, dbr.ErrInvalidPointer
    //}
    count := 0
    for rows.Next() {
        scanResultContainers := make([]interface{}, len(columns))
        for i := 0; i < len(columns); i++ {
            var scanResultContainer interface{}
            scanResultContainers[i] = &scanResultContainer
        }
        err = rows.Scan(scanResultContainers...)
        if err != nil {
            return 0, err
        }
        rowStr := make(map[string]string)
        for k, v := range columns {
            if scanResultContainers[k] == nil {
                continue
            }
            rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[k]))
            if rawValue.Interface() == nil {
                continue
            }
            resstr := ""
            resstr, err = Reflect2value(&rawValue, false)
            if err != nil {
                return 0, err
            }
            rowStr[v] = resstr
        }
        var elem reflect.Value
        if isSlice {
            elem = reflect.New(v.Type().Elem()).Elem()
        } else {
            elem = reflect.New(v.Type()).Elem()
        }
        destPtrMap := make(map[string]interface{})
        err = mapStr2Struct(rowStr, elem, destPtrMap)
        if err != nil {
            return 0, err
        }
        count++
        if isSlice {
            if v.Len() == 0 {
                v.Set(reflect.Append(v, elem))
            } else {
                vv := v.Index(v.Len() - 1)
                if !togetherSlice(elem, vv) {
                    v.Set(reflect.Append(v, elem))
                }
            }
        } else {
            if count == 1 {
                v.Set(elem)
            } else {
                if !togetherSlice(elem, v) {
                    return 0, errors.New("传入对象不是一个数组,合并后超过两条")
                }
            }
        }
    }
    return count, nil
}

相关文章

网友评论

      本文标题:golang操作mysql聚合结果

      本文链接:https://www.haomeiwen.com/subject/glfrdktx.html