我正在使用Go与GORM ORM.我有以下结构.关系很简单.One Town有多个地方,一个地方属于一个城镇.
type Place struct {
ID int
Name string
Town Town
}
type Town struct {
ID int
Name string
}
Run Code Online (Sandbox Code Playgroud)
现在我想查询所有地方,并与他们所有的领域相处相应的城镇的信息.这是我的代码:
db, _ := gorm.Open("sqlite3", "./data.db")
defer db.Close()
places := []Place{}
db.Find(&places)
fmt.Println(places)
Run Code Online (Sandbox Code Playgroud)
我的示例数据库有这些数据:
/* places table */
id name town_id
1 Place1 1
2 Place2 1
/* towns Table */
id name
1 Town1
2 Town2
Run Code Online (Sandbox Code Playgroud)
我收到了这个:
[{1 Place1 {0 }} {2 Mares Place2 {0 }}]
但是我期待收到这样的东西(这两个地方都属于同一个城镇):
[{1 Place1 {1 Town1}} {2 Mares Place2 {1 Town1}}]
我怎么能这样查询?我尝试使用Preloads
,并Related
没有成功(可能是错误的方式).我无法达到预期的效果.
Jav*_*diz 46
TownID
必须指定为外键.该Place
结构得到这样的:
type Place struct {
ID int
Name string
Description string
TownID int
Town Town
}
Run Code Online (Sandbox Code Playgroud)
现在有不同的方法来处理这个问题.例如:
places := []Place{}
db.Find(&places)
for i, _ := range places {
db.Model(places[i]).Related(&places[i].Town)
}
Run Code Online (Sandbox Code Playgroud)
这肯定会产生预期的结果,但请注意日志输出和触发的查询.
[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}]
Run Code Online (Sandbox Code Playgroud)
输出是预期的,但这种方法有一个根本的缺陷,请注意,对于每个地方,都需要进行另一个产生n + 1
问题的数据库查询.这可以解决问题,但随着地方数量的增加,很快就会失控.
事实证明,使用预加载的好方法相当简单.
db.Preload("Town").Find(&places)
Run Code Online (Sandbox Code Playgroud)
就是这样,生成的查询日志是:
[22.24ms] SELECT * FROM "places"
[0.92ms] SELECT * FROM "towns" WHERE ("id" in ('1'))
[{1 Place1 {1 Town1} 1} {2 Place2 {1 Town1} 1}]
Run Code Online (Sandbox Code Playgroud)
这种方法只会触发两个查询,一个用于所有地方,一个用于所有有地方的城镇.这种方法可以很好地衡量地点和城镇的数量(在所有情况下只有两个查询).
您没有在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)
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
为了优化查询,我在相同情况下使用“ in condition”
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]
}
}
Run Code Online (Sandbox Code Playgroud)