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

如何用Goqu关联用户与订单表并扫描结果至嵌套结构体

单条SQL查询关联用户与订单并映射到结构体解决方案

首先明确结构体定义(以Go语言为例,假设使用标准database/sql包):

type Order struct {
    ID         int     `db:"id"`
    UserID     int     `db:"user_id"`
    OrderValue float64 `db:"order_value"`
}

type SelectUserWithOrders struct {
    ID       int     `db:"id"`
    Name     string  `db:"name"`
    Password string  `db:"password"`
    Orders   []Order `db:"-"` // 标记为跳过直接SQL扫描,后续手动填充
}

编写关联查询SQL

使用LEFT JOIN确保无订单的用户也能被查询到,同时一次性获取所有关联数据:

SELECT 
    u.id, u.name, u.password,
    o.id, o.user_id, o.order_value
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = ?;

扫描结果并聚合订单

通过内存中的map缓存用户实例,将同一用户的多条订单记录聚合到Orders数组中,避免循环查询:

func GetUserWithOrders(db *sql.DB, userID int) (*SelectUserWithOrders, error) {
    sqlQuery := `
        SELECT 
            u.id, u.name, u.password,
            o.id, o.user_id, o.order_value
        FROM users u
        LEFT JOIN orders o ON u.id = o.user_id
        WHERE u.id = ?;
    `

    rows, err := db.Query(sqlQuery, userID)
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    userMap := make(map[int]*SelectUserWithOrders)

    for rows.Next() {
        var user SelectUserWithOrders
        var order Order
        // 扫描字段顺序需与SELECT语句严格一致
        err := rows.Scan(
            &user.ID, &user.Name, &user.Password,
            &order.ID, &order.UserID, &order.OrderValue,
        )
        if err != nil {
            return nil, err
        }

        existingUser, exists := userMap[user.ID]
        if exists {
            // 跳过LEFT JOIN返回的空订单记录(无订单时order.ID为0或NULL)
            if order.ID != 0 {
                existingUser.Orders = append(existingUser.Orders, order)
            }
        } else {
            // 初始化用户的订单切片
            if order.ID != 0 {
                user.Orders = []Order{order}
            } else {
                user.Orders = []Order{}
            }
            userMap[user.ID] = &user
        }
    }

    if err := rows.Err(); err != nil {
        return nil, err
    }

    // 提取结果(因查询条件为单一用户ID,map中最多一个元素)
    var result *SelectUserWithOrders
    for _, u := range userMap {
        result = u
        break
    }

    return result, nil
}

关键说明

  1. 单条查询优势:仅与数据库交互一次,避免N+1查询的性能损耗
  2. NULL值处理:通过判断order.ID是否有效,过滤LEFT JOIN产生的空订单记录(若数据库字段允许NULL,建议使用sql.NullInt64等类型更严谨处理)
  3. 内存聚合:利用map缓存用户实例,将同一用户的多条订单记录合并到Orders数组中,最终得到包含完整订单列表的用户结构体

内容的提问来源于stack exchange,提问作者raj.dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:15:18