【问题标题】:Scan SQL NULL values in golang在 golang 中扫描 SQL NULL 值
【发布时间】:2017-06-21 08:03:03
【问题描述】:

 使用标准数据库/sql Row.Scan() 我在行中遇到空值问题。如果不考虑连续可能的空值,我会得到像<nil> -> *string 这样的扫描错误。这在使用 LEFT JOIN 查询或缺少 NO NULL 列约束的弱定义表时很常见。

sql.NullXXX 类型(例如sql.NullInt64)可用于从一行中扫描可能的空值,然后检查该值是否为.Valid。但是,这些类型没有实现 JSON 编组,需要更多的逻辑和编码。

要解决这个问题,是在 SQL 查询中处理COALESCE() 列值更好的方法还是在 golang 代码中进行一些额外的编码?

【问题讨论】:

  • 请参阅此SO post 自行处理可为空的值。建议:你可以试试这个库github.com/guregu/null
  • SQL 解决方案怎么样?我可以看到一些专业人士在查询中使用 COALESCE() 值。您可以将值直接扫描为标准类型,也可以定义零以外的默认值。
  • 在这两种情况下,您都必须采取一些额外措施来处理 null 并在适当的地方分配默认值。如果我看它,这是您的设计选择。您想在“分离关注点”中划清界限。应用代码还是数据库查询?

标签: sql json go


【解决方案1】:

您可以为 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 中使用它非常有帮助。

【讨论】:

    【解决方案2】:

    作为一个选项,您可以实现与JSON Marshaler 接口匹配的自定义数据类型。之后,您将能够使用常规标签编组您的结构。

    查看示例:

    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"`
    }
    

    【讨论】:

    • 我也在 Golang repo 中发现了这个问题。显然,他们不想混淆将 JSON 功能包含到 sql lib 中的人。你可以在这里找到更多信息:github.com/golang/go/issues/9719
    【解决方案3】:
    // 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
    }
    

    【讨论】:

      猜你喜欢
      • 2017-10-26
      • 1970-01-01
      • 2012-01-31
      • 1970-01-01
      • 2020-02-15
      • 1970-01-01
      • 2019-09-28
      • 2014-11-22
      • 2018-02-27
      相关资源
      最近更新 更多