技術向上

プログラミングの学び、気になるテクノロジーやビジネストレンドを発信

SQLによる取得結果をHas Manyの形で格納する方法【Go】

SQLの取得結果をHas Manyの形で格納したいことがあるかもしれません。しかし、SQLでのSELECTの結果は階層化されていないので、ソースコードでロジックを組んで組み立てる必要があります。

Goの場合、SQLの取得結果を1行ずつ処理できるrow.Next()があるので、用意した構造体に格納するロジックをソースコードで組めば、Has Manyを実現することができます。

type Group struct {
    GroupID   int                `json:"group_id"`
    GroupName string             `json:"group_name"`
    Workers  []Worker`json:"workers"`
}

type Worker struct {
    WorkerID   int                `json:"worker_id"`
    WorkerName string             `json:"worker_name"`
    Jobs  []Job`json:"jobs"`
}

type Job struct {
    JobID   int            `json:"job_id"`
    JobName string         `json:"job_name"`
}

func main() {
    rtns := []*Group{}
    cnn := sql.Open("mysql", "...")
    rows, err := cnn.Query("SELECT ... FROM ...")

    for rows.Next() {
        group := &Group{}
        worker := &Worker{}
        job := &Job{}
        err := rows.Scan(
            &group.GroupID,
            &group.GroupName,
            &worker.WorkerID,
            &worker.WorkerName,
            &job.JobID,
            &job.JobName,
        )
        if err != nil {
            ...
            return nil, err
        }

        rtns = buildGroups(rtns, group, worker, job)
    }
    return rtns, nil
}

// 前回のrowの内容と比較して、IDが一致する場合は前回のrowの格納結果のsliceに追加して階層構造を作る関数
func buildGroups(rtns []*Group, group *Group, worker *Worker, job *Job) []*Group {
    if len(rtns) > 0 {
        lastW := rtns[len(rtns)-1].Workers
        lastWorkerID := lastW[len(lastW)-1].WorkerID

        if worker.WorkerID == lastWorkerID {
            lastW[len(lastW)-1].Jobs = append(lastW[len(lastW)-1].Jobs, *job)
        } else {
            worker.Jobs = append(worker.Jobs, *job)
            lastGroupID := rtns[len(rtns)-1].GroupID

            if group.GroupID == lastGroupID {
                rtns[len(rtns)-1].Workers = append(lastW, *worker)
            } else {
                group.Workers = append(group.Workers, *worker)
                rtns = append(rtns, group)
            }
        }
    } else {
        worker.Jobs = append(worker.Jobs, *job)
        group.Workers = append(group.Workers, *worker)

        rtns = append(rtns, group)
    }
    return rtns
}