问题
在处理Golang中的一对多或多对多SQL关系时,将行映射到结构体的最佳(高效,推荐,“Go-like”)方法是什么?
在下面的示例设置中,我已经尝试详细说明了一些方法以及每种方法的优缺点,但想知道社区的建议。
要求
- 与PostgreSQL配合使用(可以是通用的,但不包括MySQL / Oracle特定功能)
- 效率-不要蛮力每个组合
- 没有ORM-理想情况下仅使用
database/sql
和jmoiron/sqlx
示例
为了清晰起见,我已删除了错误处理
模型
type Tag struct {
ID int
Name string
}
type Item struct {
ID int
Tags []Tag
}
数据库
CREATE TABLE item (
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
);
CREATE TABLE tag (
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR(160),
item_id INT REFERENCES item(id)
);
方法1 - 选择所有项目,然后为每个项目选择标签
var items []Item
sqlxdb.Select(&items, "SELECT * FROM item")
for i, item := range items {
var tags []Tag
sqlxdb.Select(&tags, "SELECT * FROM tag WHERE item_id = $1", item.ID)
items[i].Tags = tags
}
优点
- 简单易懂
- 容易理解
缺点
- 随着项目数量的增加,数据库查询次数呈比例增长,效率低下
方法2 - 构建SQL连接并手动循环行
var itemTags = make(map[int][]Tag)
var items = []Item{}
rows, _ := sqlxdb.Queryx("SELECT i.id, t.id, t.name FROM item AS i JOIN tag AS t ON t.item_id = i.id")
for rows.Next() {
var (
itemID int
tagID int
tagName string
)
rows.Scan(&itemID, &tagID, &tagName)
if tags, ok := itemTags[itemID]; ok {
itemTags[itemID] = append(tags, Tag{ID: tagID, Name: tagName,})
} else {
itemTags[itemID] = []Tag{Tag{ID: tagID, Name: tagName,}}
}
}
for itemID, tags := range itemTags {
items = append(Item{
ID: itemID,
Tags: tags,
})
}
优点
- 只需进行一次数据库调用和游标循环,不会占用太多内存。
缺点
- 多个连接和许多结构属性使它复杂且难以开发。
- 性能不佳,相比于更多的网络调用,会使用更多的内存和处理时间。
失败的尝试3 - sqlx struct scanning
虽然失败,但我仍希望包括这种方法,因为我认为这是目前在开发简单性与效率方面的最佳选择。 我的希望是通过显式设置每个结构字段上的db
标签,sqlx
可以进行一些高级结构扫描。
var items []Item
sqlxdb.Select(&items, "SELECT i.id AS item_id, t.id AS tag_id, t.name AS tag_name FROM item AS i JOIN tag AS t ON t.item_id = i.id")
很不幸,这会报错,错误信息为
在*[]Item中缺少目标名称tag_id
,这让我认为StructScan
不足以递归地循环遍历行(没有批评之意——这是一个复杂的场景)。可能的解决方案4 - PostgreSQL数组聚合器和
GROUP BY
虽然我确定这不会起作用,但我已经包含了这个未经测试的选项,看看它是否可以改进,以便它能够工作。var items = []Item{}
sqlxdb.Select(&items, "SELECT i.id as item_id, array_agg(t.*) as tags FROM item AS i JOIN tag AS t ON t.item_id = i.id GROUP BY i.id")
我有时间的话,会在这里尝试运行一些实验。
ID int
和一个Tags []Tag
数组。实际上,这要复杂得多。 - Ewan