Golang中基于API返回数据关联数据库表的优化实现问询
优化Golang数据合并方案的建议
问题背景
现有数据库表结构如下:
Industry表
| industry_id | name |
|---|---|
| 1 | industry A |
| 2 | industry B |
Guide表
| industry_id | guide_id | name |
|---|---|---|
| 1 | 1 | guide A |
| 1 | 2 | guide B |
| 1 | 3 | guide C |
| 1 | 4 | guide D |
| 2 | 11 | guide E |
此前依赖Summary表关联查询获取目标格式数据,原简化SQL存在语法错误,修正后如下:
select s.industry_id, case when s.guide_id is null then null else g.guide_id end as guide_id, case when s.guide_id is null then i.name else g.name end as name, s.uuid, case when s.guide_id is null then null else (uuid of parent) end as parent_uuid from summary s left join industry i on s.industry_id = i.industry_id left join guide g on s.industry_id = g.industry_id and s.guide_id = g.guide_id where s.industry_id = 4;
现在无法使用Summary表,需改用API接口获取基础数据,API返回示例:
[ { "industry_id": 1, "guide_id": null, "uuid": "AAAA" }, { "industry_id": 1, "guide_id": 1, "uuid": "BBBB" }, { "industry_id": 1, "guide_id": 2, "uuid": "CCCC" }, { "industry_id": 1, "guide_id": 3, "uuid": "DDDD" } ]
当前设想的Golang实现步骤:
- 调用API获取响应数据;
- 提取响应中的guide_id列表;
- 执行SQL查询Guide表信息:
select * from guide where industry_id=4 and guide_id in (<步骤2生成的列表>); - 执行SQL查询Industry表信息:
select * from industry where industry_id=4; - 将步骤4的记录合并到步骤3的结果中;
- 补充API返回的UUID信息后返回最终结果。
希望找到更简洁、优雅的实现方式。
优化实现方案
1. 合并数据库查询,减少交互次数
把Industry和Guide的查询用UNION ALL合并成一次SQL请求,避免两次数据库调用,降低开销:
SELECT industry_id, guide_id, name FROM guide WHERE industry_id = 4 AND guide_id IN (<提取的非空guide_id列表>) UNION ALL SELECT industry_id, NULL AS guide_id, name FROM industry WHERE industry_id = 4;
如果API返回的guide_id存在重复,先对其去重再生成IN列表,可以进一步缩小查询范围。
2. 构建内存映射,高效匹配数据
在Golang中,将数据库查询结果转换成键值对映射,遍历API返回数据时直接匹配,避免多次循环查找:
// 定义映射的键结构体,用指针区分guide_id的null和0值 type NameMapKey struct { IndustryID int GuideID *int } // 定义数据库查询结果的结构体 type DBNameItem struct { IndustryID int `db:"industry_id"` GuideID *int `db:"guide_id"` Name string `db:"name"` } // 定义最终返回的结构体 type FinalResult struct { IndustryID int `json:"industry_id"` GuideID *int `json:"guide_id"` Name string `json:"name"` UUID string `json:"uuid"` ParentUUID *string `json:"parent_uuid"` } func processData(apiResponse []APIData) ([]FinalResult, error) { // 1. 提取API中的非空guide_id并去重 guideIDs := make(map[int]struct{}) var industryID int for _, item := range apiResponse { industryID = item.IndustryID if item.GuideID != nil { guideIDs[*item.GuideID] = struct{}{} } } // 2. 生成IN列表的参数,执行合并后的SQL查询 idList := make([]int, 0, len(guideIDs)) for id := range guideIDs { idList = append(idList, id) } var dbItems []DBNameItem query := ` SELECT industry_id, guide_id, name FROM guide WHERE industry_id = $1 AND guide_id = ANY($2) UNION ALL SELECT industry_id, NULL AS guide_id, name FROM industry WHERE industry_id = $1; ` if err := db.Select(&dbItems, query, industryID, idList); err != nil { return nil, err } // 3. 构建名称映射字典 nameMap := make(map[NameMapKey]string) for _, item := range dbItems { key := NameMapKey{ IndustryID: item.IndustryID, GuideID: item.GuideID, } nameMap[key] = item.Name } // 4. 遍历API数据,组装最终结果 finalResults := make([]FinalResult, 0, len(apiResponse)) for _, apiItem := range apiResponse { key := NameMapKey{ IndustryID: apiItem.IndustryID, GuideID: apiItem.GuideID, } name := nameMap[key] var parentUUID *string if apiItem.GuideID != nil { // 根据业务逻辑获取父UUID,比如从缓存或其他映射中获取 parentUUIDVal := "父UUID示例值" parentUUID = &parentUUIDVal } finalResults = append(finalResults, FinalResult{ IndustryID: apiItem.IndustryID, GuideID: apiItem.GuideID, Name: name, UUID: apiItem.UUID, ParentUUID: parentUUID, }) } return finalResults, nil }
3. 可选优化:并发执行API请求与数据库查询
如果API请求和数据库查询的参数仅依赖industry_id(无需先获取API返回的guide_id),可以用goroutine并发执行两个操作,减少整体处理耗时:
func fetchDataConcurrently(industryID int) ([]APIData, []DBNameItem, error) { var apiResp []APIData var dbItems []DBNameItem errChan := make(chan error, 2) // 并发调用API go func() { resp, err := callAPI(industryID) if err != nil { errChan <- err return } apiResp = resp errChan <- nil }() // 并发查询数据库 go func() { items, err := queryDB(industryID) if err != nil { errChan <- err return } dbItems = items errChan <- nil }() // 等待两个任务完成 for i := 0; i < 2; i++ { if err := <-errChan; err != nil { return nil, nil, err } } return apiResp, dbItems, nil }
内容的提问来源于stack exchange,提问作者Erdinc
相关产品推荐
相关产品推荐

