扫描golang中的SQL NULL值

bvjveswy  于 2022-12-07  发布在  Go
关注(0)|答案(4)|浏览(129)

使用标准数据库/sql Row.Scan()时,我遇到了行中存在空值的问题。如果不考虑行中可能存在的空值,我可能会得到类似<nil> -> *string的扫描错误。这在使用LEFT JOIN查询或缺少NO NULL列约束的弱定义表时很常见。
有一些sql.NullXXX类型(例如sql.NullInt64)可用于扫描行中可能的空值,然后检查该值是否为.Valid。但是,这些类型不实现JSON编组,需要更多的逻辑和编码。
为了解决这个问题,是在SQL查询中使用COALESCE()列值更好,还是在golang代码中进行一些额外的编码更好?

y53ybaqx

y53ybaqx1#

您可以使用sql包提供的类型的别名,如(NullInt64、NullString等...)。使用别名有两个好处,一是可以扫描空值并在golang结构中获取,二是可以在JSON中封送该结构。
请看示例代码:

// NullInt64 is an alias for sql.NullInt64 data type
type NullInt64 sql.NullInt64

// Scan implements the Scanner interface for NullInt64
func (ni *NullInt64) Scan(value interface{}) error {
    var i sql.NullInt64
    if err := i.Scan(value); err != nil {
        return err
    }
    // if nil the make Valid false
    if reflect.TypeOf(value) == nil {
        *ni = NullInt64{i.Int64, false}
    } else {
        *ni = NullInt64{i.Int64, true}
    }
    return nil
}

// MarshalJSON for NullInt64
func (ni *NullInt64) MarshalJSON() ([]byte, error) {
    if !ni.Valid {
        return []byte("null"), nil
    }
    return json.Marshal(ni.Int64)
}

请看一下这个article,它对在golang中处理空值以及如何在JSON中使用它非常有帮助。

rdlzhqv9

rdlzhqv92#

作为一种选择,你可以实现与JSON封送器接口匹配的自定义数据类型。之后,你就可以使用常规标记封送你的结构了。
检查示例:

type UserTitleType sql.NullString

func (s UserTitleType) MarshalJSON() ([]byte, error) {
    if s.Valid {
        return json.Marshal(s.String)
    }
    return jsonNull, nil
}

type User struct {
    Id    int64         `json:"id"`
    Title UserTitleType `json:"title"`
}
vq8itlhq

vq8itlhq3#

// Scan implements the Scanner interface.
func (n *NullInt64) Scan(value interface{}) error {
    var ok bool
    if value == nil {
        n.Int64, n.Valid = 0, false
        return nil
    }

    n.Valid = true
    n.Int64, ok = value.(int64)
    if !ok {
        return errors.New("convert error")
    }
    return nil
}

// Value implements the driver Valuer interface.
func (n NullInt64) Value() (driver.Value, error) {
    if !n.Valid {
        return nil, nil
    }
    return n.Int64, nil
}
plicqrtu

plicqrtu4#

我实现了这种方法,它允许您扫描行,其中nil值被扫描为零值,尽管您必须将rows.Scan(args...)替换为RowScan(rows,args...)。而且,这种实现仅支持string、int和bool。如果需要,您可以从sql.Nullxxx添加其他类型

func RowScan(row *sql.Rows, args ...interface{}) error {

    scanArgs := make([]interface{}, len(args))

    for i, arg := range args {

        switch reflect.ValueOf(arg).Elem().Kind() {

        case reflect.Int64, reflect.Int:
            scanArgs[i] = &sql.NullInt64{}
        case reflect.Bool:
            scanArgs[i] = &sql.NullBool{}
        case reflect.String:
            scanArgs[i] = &sql.NullString{}
        default:
            return errors.New("unsupported type")
        }
    }
    err := row.Scan(scanArgs...)

    if err != nil {
        return err
    }

    for i, sArg := range scanArgs {

        switch reflect.ValueOf(sArg).Elem().Field(0).Kind() {

        case reflect.Int64, reflect.Int:
            reflect.ValueOf(args[i]).Elem().SetInt(scanArgs[i].(*sql.NullInt64).Int64)
        case reflect.Bool:
            reflect.ValueOf(args[i]).Elem().SetBool(scanArgs[i].(*sql.NullBool).Bool)
        case reflect.String:
            reflect.ValueOf(args[i]).Elem().SetString(scanArgs[i].(*sql.NullString).String)
        default:
            return errors.New("unsupported type")
        }
    }
    return nil
}

相关问题