You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将扁平化查询结果映射到含数组的嵌套Go结构体?

问题:将扁平化数据库记录映射到Go嵌套结构体

我定义了如下Go结构体:

type Product struct {
    ID         int           `json:"id"`
    Name       string        `json:"name"`
    Dimensions []Dimensions `json:"dimensions"`
}

type Dimensions struct {
    Length int `json:"length"`
    Width  int `json:"width"`
    Height int `json:"height"`
}

数据以扁平化形式存储,每组Dimensions对应重复的ID和Name(ID、Name与Dimensions是一对多关系),例如ID为1、名称为ABC的产品对应两组尺寸:

IDNameLengthWidthHeight
1ABC123
1ABC456

请问如何通过StructScan或其他方法,将数据正确映射到嵌套结构体中,使Dimensions数组正确嵌套?


解决方案

直接用StructScan无法自动处理一对多的嵌套映射——它会把每一行记录单独映射为一个结构体实例,导致重复生成Product对象。我们需要先查询所有扁平化数据,再通过分组逻辑合并嵌套的Dimensions数组,以下是两种可行方案:

方法1:临时结构体+分组合并(最直接高效)

  1. 定义与数据库表字段一一对应的临时结构体:
type FlatProduct struct {
    ID     int    `db:"id"`
    Name   string `db:"name"`
    Length int    `db:"length"`
    Width  int    `db:"width"`
    Height int    `db:"height"`
}
  1. 查询所有数据到临时结构体切片:
var flatProducts []FlatProduct
err := db.Select(&flatProducts, "SELECT id, name, length, width, height FROM products WHERE id = ?", 1)
if err != nil {
    // 此处处理查询错误
}
  1. 用map按ID分组,合并同ID产品的Dimensions:
productMap := make(map[int]*Product)
for _, fp := range flatProducts {
    if p, exists := productMap[fp.ID]; exists {
        // 产品已存在,追加新尺寸
        p.Dimensions = append(p.Dimensions, Dimensions{
            Length: fp.Length,
            Width:  fp.Width,
            Height: fp.Height,
        })
    } else {
        // 首次遇到该产品,初始化结构体
        productMap[fp.ID] = &Product{
            ID:       fp.ID,
            Name:     fp.Name,
            Dimensions: []Dimensions{
                {
                    Length: fp.Length,
                    Width:  fp.Width,
                    Height: fp.Height,
                },
            },
        }
    }
}

// 若需要切片格式,转换map值为切片
var products []Product
for _, p := range productMap {
    products = append(products, *p)
}

方法2:自定义扫描逻辑(适用于sqlx库)

如果你使用sqlx,可以实现sql.Scanner接口处理嵌套,但本质仍需配合分组逻辑去重,适合需要复用扫描逻辑的场景:

func (p *Product) Scan(src interface{}) error {
    row, ok := src.([]interface{})
    if !ok {
        return fmt.Errorf("expected []interface{}, got %T", src)
    }

    // 解析数据库返回的字段值
    id, _ := row[0].(int64)
    name, _ := row[1].(string)
    length, _ := row[2].(int64)
    width, _ := row[3].(int64)
    height, _ := row[4].(int64)

    p.ID = int(id)
    p.Name = name
    p.Dimensions = append(p.Dimensions, Dimensions{
        Length: int(length),
        Width:  int(width),
        Height: int(height),
    })
    return nil
}

使用该自定义扫描时,仍需在查询后用map对结果去重合并,否则会生成多个同ID的Product实例。


内容的提问来源于stack exchange,提问作者user3277752

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 04:09:20