过滤和排序SQL查询以重新创建嵌套结构。

huangapple go评论89阅读模式
英文:

Filtering and sorting an SQL query to recreate a nested struct

问题

我是新手学习Go语言,我想从一个SQL查询中填充一个名为Reliefworker的结构体,并将其作为JSON数据发送。

基本上,我有一个Reliefworker可能被分配到多个社区,而社区可以包含多个地区。

我怀疑有一种聪明的方法可以做到这一点,而不是我打算采用的原始解决方案,该解决方案将在SQL中添加一个排序(按社区),并创建一个函数来检测要添加的社区是否与上一个社区不同,如果是,则创建一个新的社区结构体对象进行追加。

type Reliefworker struct {
    Name     string `json:"name"`
    Communities  []Community `json:"community"`
    Deployment_id    string `json:"deployment_id"`
}

type Community struct{
    Name     string `json:"name"`
    community_id     string `json:"community_id"`
    Regions []Region `json:"regions"`
}

type Region struct{
    Name     string `json:"name"`
    Region_id     string `json:"region_id"`
    Reconstruction_grant   string `json:"reconstruction_grant"`
    Currency string `json:"currency"`
}

目前,我已经创建了一个反映我从SQL中实际获取到的内容的结构体,同时思考着下一步该怎么做。也许这可能是一个好的过渡阶段,而不是尝试进行即时转换。

type ReliefWorker_community_region struct {
	Deployment_id        string
	Community_title      int
	Region_name          string
	Reconstruction_grant int
}

func GetReliefWorkers(deployment_id string) []Reliefworker {

	fmt.Printf("确认我有一个部署ID:%v\n", deployment_id)

	rows, err := middleware.Db.Query("select deployment_id, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", brand_id)

	if err != nil {
		return
	}

	for rows.Next() {
		reliefworker := Reliefworker{}
		err = rows.Scan(&deployment_id, &community_title, &region_name, &reconstruction_grant)
		if err != nil {
			return
		}
	}
	rows.Close()

	return
}
英文:

I am new to Go and I'm trying to populate a struct called Reliefworker from an SQL query which I can send as a JSON payload.

Essentially I have a Reliefworker who may be assigned to many communities and the community can consist of multiple regions.

I suspect there is a clever way of doing this other than what I intend which is a primitive solution that will add a sort to the SQL (by Community) and create a function that detects if the community being added is different to the previous one, in which case I would create a new community struct type object to be appended.

type Reliefworker struct {
    Name     string `json:"name"`
    Communities  []Community `json:"community"`
    Deployment_id    string `json:"deployment_id"`
 }

 type Community struct{
    Name     string `json:"name"`
    community_id     string `json:"community_id"`
    Regions []Region `json:"regions"`
}

type Region struct{
    Name     string `json:"name"`
    Region_id     string `json:"region_id"`
    Reconstruction_grant   string `json:"reconstruction_grant"`
    Currency string `json:"currency"`
}

Currently I have created a struct that reflects what I am actually getting from SQL whilst pondering my next move. Perhaps this might be a good stepping stone instead of attempting an on-the-fly transformation ?

type ReliefWorker_community_region struct {
	Deployment_id        string
	Community_title      int
	Region_name          string
	Reconstruction_grant int
}

func GetReliefWorkers(deployment_id string) []Reliefworker {

	fmt.Printf("Confirm I have a deployment id:%v\n", deployment_id)

	rows, err := middleware.Db.Query("select deployment_id, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", brand_id)

	if err != nil {
		return
	}

	for rows.Next() {
		reliefworker := Reliefworker{}
		err = rows.Scan(&deployment_id, &community_title, &region_name, &reconstruction_grant)
		if err != nil {
			return
		}
	}
	rows.Close()

	return
}

答案1

得分: 1

我认为排序是有意义的,原始解决方案可能是最有效的:

func GetReliefWorkers(deployment_id string) []Reliefworker {
    // 添加排序到查询语句
    q := "select worker_name, community_title, region_name, reconstruction_grant WHERE deployment_id=? ORDER BY community_title"    
    rows, err := middleware.Db.Query(q, deployment_id)
    if err != nil {
        return
    }
    defer rows.Close() // 即使出现错误,也要关闭rows
    c := Community{} // 用于跟踪当前社区
    cmatrix := [][]string{[]string{}}  // 社区和工作人员的矩阵
    communities := []Community{} // 社区列表
    workers := make(map[string]Reliefworker) // 工作人员映射
    var ccount int // 社区在列表中的索引
    for rows.Next() {
        w := Reliefworker{Deployment_id: deployment_id}
        r := Region{}
        var ctitle string  // 用于后续比较
        err = rows.Scan(&w.Name, &ctitle, &r.Name, &r.Reconstruction_grant)
        if err != nil {
            return
        }
        if ctitle != c.Name {
            communities = append(communities, c)
            c = Community{}
            c.Name = ctitle
            ccount++
            cmatrix = append(cmatrix, []string{})
        }
        c.Regions = append(c.Regions, r)
        cmatrix[ccount] = append(cmatrix[ccount], w.Name)
        workers[w.Name] = w
    }
    for i, c := range communities {
        for _, id := range cmatrix[i] {
            w := workers[id] // 避免错误
            w.Communities = append(w.Communities, c)
            workers[id] = w
        }
    }
    out := []Reliefworker{}
    for _, w := range workers {
        out = append(out, w)
    }
    return out
}

不过,创建单独的表格用于社区、地区和工作人员可能更有意义,然后使用JOIN查询它们:https://www.w3schools.com/sql/sql_join_inner.asp

更新:由于您只想检索一个Reliefworker,像这样的代码是否可行?

type ReliefWorker struct {
	Name        string      `json:"name"`
	Communities []Community `json:"community"`
}

type Community struct {
	Name    string   `json:"name"`
	Regions []Region `json:"regions"`
}

type Region struct {
	Name                 string `json:"name"`
	Region_id            string `json:"region_id"`
	Reconstruction_grant int    `json:"reconstruction_grant"`
	Currency             string `json:"currency"`
}

func GetReliefWorkers(deployment_id string) Reliefworker {
	reliefworker := Reliefworker{}
	communities := make(map[string]Community)
	rows, err := middleware.Db.Query("select name, community_title, region_name, region_id, reconstruction_grant WHERE Deployment_id=$1", deployment_id)
	if err != nil {
		if err == sql.ErrNoRows {
			fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
		}
		panic(err)
	}
	defer rows.Close()
	for rows.Next() {
		c := Community{}
		r := Region{}
		err = rows.Scan(&reliefworker.Name, &c.Name, &r.Name, &r.Region_id, &r.Reconstruction_grant)
		if err != nil {
			panic(err)
		}
		if _, ok := communities[c.Name]; ok {
			c = communities[c.Name]
		}
		c.Regions = append(c.Regions, r)
		communities[c.Name] = c
	}
	for _, c := range commmunities {
		reliefworker.Communities = append(reliefworker.Communities, c)
	}
	return reliefworker
}
英文:

I think a sort makes a lot of sense, primitive solutions can be the most efficient:

func GetReliefWorkers(deployment_id string) []Reliefworker {
    // Added sort to query
    q := "select worker_name, community_title, region_name, reconstruction_grant WHERE deployment_id=? ORDER BY community_title"    
    rows, err := middleware.Db.Query(q, deployment_id)
    if err != nil {
        return
    }
    defer rows.Close() // So rows get closed even on an error
    c := Community{} // To keep track of the current community
    cmatrix := [][]string{[]string{}}  // Matrix of communities and workers
    communities := []Community{} // List of communities
    workers := make(map[string]Reliefworker) // Map of workers
    var ccount int // Index of community in lists
    for rows.Next() {
        w := Reliefworker{Deployment_id: deployment_id}
        r := Region{}
        var ctitle string  // For comparison later
        err = rows.Scan(&w.Name, &ctitle, &r.Name, &r.Reconstruction_grant)
        if err != nil {
            return
        }
        if ctitle != c.Name {
            communities = append(communities, c)
            c = Community{}
            c.Name = ctitle
            ccount++
            cmatrix = append(cmatrix, []string{})
        }
        c.Regions = append(c.Regions, r)
        cmatrix[ccount] = append(cmatrix[ccount], w.Name)
        workers[w.Name] = w
    }
    for i, c := range communities {
        for _, id := range cmatrix[i] {
            w := workers[id] // To avoid error 
            w.Communities = append(w.Communities, c)
            workers[id] = w
        }
    }
    out := []Reliefworker{}
    for _, w := range workers {
        out = append(out, w)
    }
    return out
}

Though it might make even more sense to create seperate tables for communities, regions, and workers, then query them all with a JOIN: https://www.w3schools.com/sql/sql_join_inner.asp

UPDATE: Since you only want to retrieve one Reliefworker, would something like this work?

type ReliefWorker struct {
	Name        string      `json:"name"`
	Communities []Community `json:"community"`
}

type Community struct {
	Name    string   `json:"name"`
	Regions []Region `json:"regions"`
}

type Region struct {
	Name                 string `json:"name"`
	Region_id            string `json:"region_id"`
	Reconstruction_grant int    `json:"reconstruction_grant"`
	Currency             string `json:"currency"`
}

func GetReliefWorkers(deployment_id string) Reliefworker {
	reliefworker := Reliefworker{}
	communities := make(map[string]Community)
	rows, err := middleware.Db.Query("select name, community_title, region_name, region_id, reconstruction_grant WHERE Deployment_id=$1", deployment_id)
	if err != nil {
		if err == sql.ErrNoRows {
			fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
		}
		panic(err)
	}
	defer rows.Close()
	for rows.Next() {
		c := Community{}
		r := Region{}
		err = rows.Scan(&reliefworker.Name, &c.Name, &r.Name, &r.Region_id, &r.Reconstruction_grant)
		if err != nil {
			panic(err)
		}
		if _, ok := communities[c.Name]; ok {
			c = communities[c.Name]
		}
		c.Regions = append(c.Regions, r)
		communities[c.Name] = c
	}
	for _, c := range commmunities {
		reliefworker.Communities = append(reliefworker.Communities, c)
	}
	return reliefworker
}

答案2

得分: 1

好的,以下是翻译好的内容:

好的,我的简陋解决方案中没有一丝 @Absentbird 展示的智能,但我在这里学习。

@Absentbird,我喜欢你使用地图和多维数组来保存一个社区和工作者的矩阵。我将在周末专注于将这部分纳入我的工具库。

一旦我找到解决方案来解决为什么在 workers[id].Communities = append(workers[id].Communities, c) 这一行会出现错误 "cannot assign to struct field workers[id].Communities in mapcompilerUnaddressableFieldAssign",我就可以接受并调整 @Absentbird 的解决方案。

首先,我要道歉,因为我需要纠正两件事。首先,我只需要返回 ReliefWorker(而不是 ReliefWorker 数组)。其次,ReliefWorker 结构体不需要包含 Deployment_id,因为我已经知道它了。

我对 Go 还很陌生,所以我真的很希望能得到关于如何更好地利用这门语言并编写更简洁代码的反馈。

我的结构体和解决方案目前如下:

type ReliefWorker struct {
    Name        string      `json:"name"`
    Communities []Community `json:"community"`
}

type Community struct {
    Name    string   `json:"name"`
    Regions []Region `json:"regions"`
}

type Region struct {
    Name                  string `json:"name"`
    Region_id             string `json:"region_id"`
    Reconstruction_grant int    `json:"reconstruction_grant"`
    Currency              string `json:"currency"`
}

type ReliefWorker_community_region struct {
    Name                  string
    Community_title       string
    Region_name           string
    Reconstruction_grant  int
}

func GetReliefWorkers(deployment_id string) ReliefWorker {

    var reliefworker ReliefWorker
    var communitiesOnly []string
    var name string
    var allReliefWorkerData []ReliefWorker_community_region
    rows, err := middleware.Db.Query("select name, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", deployment_id)

    for rows.Next() {
        reliefWorker_community_region := ReliefWorker_community_region{}
        err = rows.Scan(&reliefWorker_community_region.Name, &reliefWorker_community_region.Community_title, &reliefWorker_community_region.Region_name, &reliefWorker_community_region.Reconstruction_grant)
        if err != nil {
            panic(err)
        }
        name = reliefWorker_community_region.Name
        allReliefWorkerData = append(allReliefWorkerData, reliefWorker_community_region)
        communitiesOnly = append(communitiesOnly, reliefWorker_community_region.Community_title)  //All communities go in here, even duplicates, will will create a unique set later
    }
    rows.Close()

    if err != nil {
        if err == sql.ErrNoRows {
            fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
        }
        panic(err)
    }

    var unique []string  //Use this to create a unique index of communities

    for _, v := range communitiesOnly {
        skip := false
        for _, u := range unique {
            if v == u {
                skip = true
                break
            }
        }
        if !skip {
            unique = append(unique, v)
        }
    }
    fmt.Println(unique)

    reliefworker.Name = name
    var community Community
    var communities []Community

    for _, v := range unique {
        community.Name = v
        communities = append(communities, community)
    }


    // Go through each record from the database held within allReliefWorkerData and grab every region belonging to a Community, when done append it to Communities and finally append that to ReliefWorker

    for j, u := range communities {
        var regions []Region

        for i, v := range allReliefWorkerData {

            if v.Community_title == u.Name {
                var region Region
                region.Name = v.Region_name
                region.Reconstruction_grant = v.Reconstruction_grant
                regions = append(regions, region)
            }
        }
        communities[j].Regions = regions

    }
    reliefworker.Communities = communities
    return reliefworker
}

希望对你有帮助!

英文:

Ok, my crude solution doesn't contain a shred of the intelligence @Absentbird demonstrates but I'm here to learn.

@Absentbird I love your use of maps and multidimensional arrays to hold a matrix of communities and workers. I will focus on making this part of my arsenal over the weekend.

I can accept and adapt @Absentbird's solution once I have a solution to why it gives the error "cannot assign to struct field workers[id].Communities in mapcompilerUnaddressableFieldAssign" for the line workers[id].Communities = append(workers[id].Communities, c)

Firstly apologies as I had to correct two things. Firstly I only needed to return ReliefWorkers (not an array of ReliefWorkers). Secondly ReliefWorker struct did not need to contain the Deployment_id since I already knew it.

I am new to Go so I'd really appreciate feedback on what I can do to better leverage the language and write more concise code.

My structs and solution is currently as follows:

type ReliefWorker struct {
Name        string      `json:"name"`
Communities []Community `json:"community"`
}
type Community struct {
Name    string   `json:"name"`
Regions []Region `json:"regions"`
}
type Region struct {
Name      string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant        int    `json:"reconstruction_grant"`
Currency  string `json:"currency"`
}
type ReliefWorker_community_region struct {
Name    string
Community_title string
Region_name     string
Reconstruction_grant  int
}
func GetReliefWorkers(deployment_id string) Reliefworker {
var reliefworker Reliefworker
var communitiesOnly []string
var name string
var allReliefWorkerData []ReliefWorker_community_region
rows, err := middleware.Db.Query("select name, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", deployment_id)
for rows.Next() {
reliefWorker_community_region := ReliefWorker_community_region{}
err = rows.Scan(&reliefWorker_community_region.Name, &reliefWorker_community_region.Community_title, &reliefWorker_community_region.Region_name, &reliefWorker_community_region.Reconstruction_grant)
if err != nil {
panic(err)
}
name = reliefWorker_community_region.Name
allReliefWorkerData = append(allReliefWorkerData, reliefWorker_community_region)
communitiesOnly = append(communitiesOnly, reliefWorker_community_region.Community_title)  //All communities go in here, even duplicates, will will create a unique set later
}
rows.Close()
if err != nil {
if err == sql.ErrNoRows {
fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
}
panic(err)
}
var unique []string  //Use this to create a unique index of communities
for _, v := range communitiesOnly {
skip := false
for _, u := range unique {
if v == u {
skip = true
break
}
}
if !skip {
unique = append(unique, v)
}
}
fmt.Println(unique)
reliefworker.Name = name
var community Community
var communities []Community
for _, v := range unique {
community.Name = v
communities = append(communities, community)
}
// Go through each record from the database held within allReliefWorkerData and grab every region belonging to a Community, when done append it to Communities and finally append that to ReliefWorker
for j, u := range communities {
var regions []Region
for i, v := range allReliefWorkerData {
if v.Community_title == u.Name {
var region Region
region.Name = v.Region_name
region.Reconstruction_grant = v.Reconstruction_grant
regions = append(regions, region)
}
}
communities[j].Regions = regions
}
reliefworker.Communities = communities
return reliefworker
}

huangapple
  • 本文由 发表于 2021年7月28日 20:02:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/68560033.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定