如何用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 }
关键说明
- 单条查询优势:仅与数据库交互一次,避免N+1查询的性能损耗
- NULL值处理:通过判断
order.ID是否有效,过滤LEFT JOIN产生的空订单记录(若数据库字段允许NULL,建议使用sql.NullInt64等类型更严谨处理) - 内存聚合:利用map缓存用户实例,将同一用户的多条订单记录合并到
Orders数组中,最终得到包含完整订单列表的用户结构体
内容的提问来源于stack exchange,提问作者raj.dev
相关产品推荐
相关产品推荐

