我使用Go GORM ORM。 我有以下结构。关系很简单。 One Town有多个地方,一个地方属于一个城镇。
type Place struct {
ID int
Name string
Town Town
}
type Town struct {
ID int
Name string
}
现在我想查询所有地方并与他们所有的领域相处相应城镇的信息。 这是我的代码:
db, _ := gorm.Open("sqlite3", "./data.db")
defer db.Close()
places := []Place{}
db.Find(&places)
fmt.Println(places)
我的示例数据库包含以下数据:
/* places table */
id name town_id
1 Place1 1
2 Place2 1
/* towns Table */
id name
1 Town1
2 Town2
我接收这个:
[{1 Place1 {0 }} {2 Mares Place2 {0 }}]
但我期待收到类似的内容(这两个地方属于同一个城镇):
[{1 Place1 {1 Town1}} {2 Mares Place2 {1 Town1}}]
我该怎么做这样的查询?我尝试使用Preloads
和Related
但没有成功(可能是错误的方式)。我无法达到预期的效果。
答案 0 :(得分:44)
TownID
指定为外键。 Place
结构如下所示:
type Place struct {
ID int
Name string
Description string
TownID int
Town Town
}
现在有不同的方法来处理这个问题。例如:
places := []Place{}
db.Find(&places)
for i, _ := range places {
db.Model(places[i]).Related(&places[i].Town)
}
这肯定会产生预期的结果,但请注意日志输出和触发的查询。
[4.76ms] SELECT * FROM "places"
[1.00ms] SELECT * FROM "towns" WHERE ("id" = '1')
[0.73ms] SELECT * FROM "towns" WHERE ("id" = '1')
[{1 Place1 {1 Town1} 1} {2 Place2 {1 Town1} 1}]
输出是预期的,但这种方法有一个根本的缺陷,请注意,对于每个地方,都需要进行另一个产生n + 1
问题的数据库查询。这可以解决问题,但随着地方数量的增加,很快就会失控。
事实证明,良好方法使用预加载非常简单。
db.Preload("Town").Find(&places)
就是这样,生成的查询日志是:
[22.24ms] SELECT * FROM "places"
[0.92ms] SELECT * FROM "towns" WHERE ("id" in ('1'))
[{1 Place1 {1 Town1} 1} {2 Place2 {1 Town1} 1}]
这种方法只会触发两个查询,一个用于所有地方,一个用于所有有地方的城镇。这种方法可以很好地衡量地点和城镇的数量(在所有情况下只有两个查询)。
答案 1 :(得分:5)
您没有在Place结构中指定城镇的外键。只需将TownId添加到Place结构中即可。
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
)
type Place struct {
Id int
Name string
Town Town
TownId int //Foregin key
}
type Town struct {
Id int
Name string
}
func main() {
db, _ := gorm.Open("sqlite3", "./data.db")
defer db.Close()
db.CreateTable(&Place{})
db.CreateTable(&Town{})
t := Town{
Name: "TestTown",
}
p1 := Place{
Name: "Test",
TownId: 1,
}
p2 := Place{
Name: "Test2",
TownId: 1,
}
err := db.Save(&t).Error
err = db.Save(&p1).Error
err = db.Save(&p2).Error
if err != nil {
panic(err)
}
places := []Place{}
err = db.Find(&places).Error
for i, _ := range places {
db.Model(places[i]).Related(&places[i].Town)
}
if err != nil {
panic(err)
} else {
fmt.Println(places)
}
}
答案 2 :(得分:5)
为了优化查询我在条件"中使用"在同样的情况下
places := []Place{}
DB.Find(&places)
keys := []uint{}
for _, value := range places {
keys = append(keys, value.TownID)
}
rows := []Town{}
DB.Where(keys).Find(&rows)
related := map[uint]Town{}
for _, value := range rows {
related[value.ID] = value
}
for key, value := range places {
if _, ok := related[value.TownID]; ok {
res[key].Town = related[value.TownID]
}
}
答案 3 :(得分:0)
无需循环获取ID,只需pluck
个ID
townIDs := []uint{}
DB.Model(&Place{}).Pluck("town_id", &placeIDs)
towns := []Town{}
DB.Where(townIDs).Find(&towns)
答案 4 :(得分:0)
首先更改您的模型:
type Place struct {
ID int
Name string
Description string
TownID int
Town Town
}
然后,进行预加载: https://gorm.io/docs/preload.html