过滤和排序 SQL 查询以重新创建嵌套结构
Posted
技术标签:
【中文标题】过滤和排序 SQL 查询以重新创建嵌套结构【英文标题】:Filtering and sorting an SQL query to recreate a nested struct 【发布时间】:2021-10-04 04:09:36 【问题描述】:我是 Go 新手,我正在尝试从可以作为 JSON 有效负载发送的 SQL 查询中填充一个名为 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("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, ®ion_name, &reconstruction_grant)
if err != nil
return
rows.Close()
return
【问题讨论】:
全局状态不容易阅读。无论如何,可能有一种方法可以在一个查询中加载所有内容,但您可以按顺序加载。我假设您对外键和一对多策略有一些概念。加载救援人员数据,然后加载并解析其所有社区,最后为每个社区加载其区域。这样你就构建了结构体。 我很欣赏这些想法,尽管很抽象。我不完全确定外键如何帮助我,因为我目前正在从一个表中提取所有内容。您是否建议我使用更简单的 SQL 表来建立有助于构建嵌套 go struct 的外键关系?这是可能的,但考虑到你的想法,我需要更多的肉在骨头上。另外,如果它使事情变得更容易,我也不热衷于拥有一个全局状态。我的直觉是,有一种干净的方法可以使用 map 和 slice 函数来做到这一点,只需少量可靠的排序函数。 或许将这个问题重新命名为“Marshalling an SQL result set into an nested Go struct”更合适?如果是这样,其他人可能需要这样做,因为我之前被警告要重新命名问题以使其更贴切。 这可能只是我对使用某些 sql 数据库可以做什么的了解不够。如果您的方法不复制任何数据,那么它可能是最佳方法。如果您想要一种将数据库输出解组为结构的干净方法,您可能应该使用一些 ORM。虽然使用 orm 并不总是答案。据我了解,您应该能够直接修改数据库中的数据,仍然使用嵌套结构而不是关系数据库是不常见的(对我来说)。拥有关系的好处是您没有重复项,并且可以轻松添加和删除项目。 你让我走上了我相信的正确道路。它可能不会很漂亮,但我正在做一些事情 【参考方案1】:我认为排序很有意义,原始解决方案可能是最有效的:
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 := ReliefworkerDeployment_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
尽管为社区、区域和工作人员创建单独的表可能更有意义,然后使用 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
【讨论】:
如果 sql 行包含一个空的社区标题,我相信,会发生一些奇怪的事情,因为如果首先出现,那么ctitle != c.Name
将返回 false。没有?
这里是communities = append(communities, c)
,它将在第一次周围的 if 语句为真时附加一个空社区。span>
是的,如果没有空白社区标题,它会先附加一个空对象,但这没关系,它只会在最后一个循环中跳过。不过,关于空白标题的好点,我会进行编辑。
另一个版本,在 Absentbird play.golang.org/p/fi8z_0xvkww 的帮助下大大改进,感谢他的审阅和 cmets。
感谢 mh-cbon,我非常感谢您的努力。这很好用,尽管在未使用的行上没有以下行:cmatrix := [][]string[]string
和 var ccount int // Index of community in lists
【参考方案2】:
好的,我的粗略解决方案不包含@Absentbird 展示的一点点智能,但我是来学习的。
@Absentbird 我喜欢您使用地图和多维数组来保存社区和工人矩阵。我将在周末专注于将这部分作为我的武器库。
我可以接受并调整@Absentbird 的解决方案,一旦我找到解决方案,为什么它会在workers[id].Communities = append(workers[id].Communities, c)
行出现错误“无法分配给 struct field workers[id].Communities in mapcompilerUnaddressableFieldAssign”
首先道歉,因为我必须纠正两件事。首先,我只需要返回 ReliefWorkers(不是 ReliefWorkers 数组)。其次,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
【讨论】:
我相信我已经通过更新解决了我的解决方案中的错误。 如果这一切都进入一个救援人员对象,这行得通吗? play.golang.org/p/Qidlk6_msGb 我在原始答案中编辑了一个改进的变体。以上是关于过滤和排序 SQL 查询以重新创建嵌套结构的主要内容,如果未能解决你的问题,请参考以下文章
UNION表时如何在Bigquery中重新排列/重新排序嵌套的重复列
如何获取将在 PHPMyAdmin 中重新创建 sql 表的查询