我有这样的数据设计:交易属于授权,而授权又属于客户。或者用结构体表示:
type Transaction struct {
shared.Transaction // transaction data
MandateID string
Mandate *Mandate
}
type Mandate struct {
shared.Mandate // mandate data
ClientID string
Client *Client
Transactions []Transaction
}
type Client struct {
shared.Client // client data
Mandates []Mandate
}
我一直在尝试使用 joins preloading 预加载交易及其授权和客户数据。 .需要注意的是,此预加载仅适用于“拥有一个”或“属于”类型的关系。
如果我只需要交易和授权数据,这会很好地工作:
var trx []rdb.Transaction
err = t.db.
Joins("Mandate").
Find(&trx, "batch_id = ?", batchID).Error
但是,一旦我尝试同时加载客户端,gorm 就会生成无效的 SQL。代码:
var trx []rdb.Transaction
err = t.db.
Joins("Mandate").
Joins("Mandate.Client").
Find(&trx, "batch_id = ?", batchID).Error
我以前使用普通预加载,但是 Preload
启动的多个查询开始成为一个严重的性能问题。用 SQL 编写整个查询并扫描结果也是一种选择,但这首先违背了使用 ORM 的目的。
我如何使用 gorm 在单个查询中获取交易及其授权和客户数据?
最佳答案
要加载嵌套关联,例如 Mandate.Client
,您需要使用 Preload
函数。
var trx []rdb.Transaction
err = t.db.
Preload("Mandate.Client").
Find(&trx, "batch_id = ?", batchID).Error
关于性能问题,是的,Preload
函数确实执行了多个查询。但是,如果您查看查询,您会发现它们已经尽可能地进行了优化。查询应如下所示:
SELECT * FROM clients WHERE id IN (23,43,12,3,2,73,121,1,9,21,39);
SELECT * FROM mandates WHERE id IN (1,2,3,4,5,6,21,43,54);
它确实扫描了整个表,但您可以看到它试图加载一组非常具体的数据。我不认为这个 ORM 能提供比这更多的帮助。事实是,如果您的批处理中有很多交易,并且您希望立即加载所有详细信息,那么您的性能会受到一些影响。
要提高您的方法的性能,您始终可以尝试通过使用 Offset
和 Limit
函数来引入分页以限制返回的事务数。或者,使用原始 SQL 查询返回非规范化结果并使用 for 循环和 Scan
读取行。
关于Gorm eager loading 加入多个关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71170220/