英文:
Join clause including soft-deleted rows
问题
我遇到了一个问题,使用go-pg
orm生成带有连接的正确的select查询语句时出现了困难,其中一个表记录可以进行软删除,而其他两个表则不行。
数据库表:
pipeline_instances | |
---|---|
instance_id int | |
pipeline_id int | |
event_id int |
pipeline_triggers | |
---|---|
id int | |
pipeline_id int | |
deleted_at timestamp |
pipeline_trigger_events | |
---|---|
event_id int | |
trigger_id int |
go-pg模型:
type pipelineTriggerEvent struct {
tableName struct{} `pg:"pipeline_trigger_events,alias:pte"`
Trigger *pipelineTrigger `pg:"rel:has-one,join_fk:id"`
PipelineInstance *pipelineInstance `pg:"rel:has-one,join_fk:event_id"`
*TriggerEvent
}
type pipelineTrigger struct {
tableName struct{} `pg:"pipeline_triggers,alias:pt"`
*Trigger
}
type pipelineInstance struct {
tableName struct{} `pg:"pipeline_pipeline_instances,alias:ppi"`
*PipelineInstance
}
我尝试生成的查询语句:
SELECT
pte.*, trigger.*, pipeline_instance.*
FROM
pipeline_trigger_events AS pte
LEFT JOIN pipeline_triggers AS trigger ON (trigger.id = pte.trigger_id)
LEFT JOIN pipeline_pipeline_instances AS pipeline_instance ON pipeline_instance.event_id = pte.event_id AND trigger.pipeline_id = pipeline_instance.pipeline_id
go-pg orm生成的查询语句:
SELECT
pte.*, trigger.*, pipeline_instance.*
FROM
pipeline_trigger_events AS pte
LEFT JOIN pipeline_triggers AS trigger ON (trigger.id = pte.trigger_id)
AND trigger.deleted_at IS NULL -- 这是不需要的行。
LEFT JOIN pipeline_pipeline_instances AS pipeline_instance ON pipeline_instance.event_id = pte.event_id AND trigger.pipeline_id = pipeline_instance.pipeline_id
var triggerevents []pipelineTriggerEvent
q := db.Model(&triggerevents).
Column("pte.*").
Relation("Trigger").
Relation("PipelineInstance", func(q *orm.Query) (*orm.Query, error) {
q = q.Join(" AND trigger.pipeline_id = pipeline_instance.pipeline_id")
return q, nil
})
在上述提到的3个表/模型中,只有pipeline_triggers表具有用于软删除的deleted_at
列。我的要求是在结果集中包括软删除的pipeline_triggers行。但是,go-pg
orm会自动在join
子句中添加trigger.deleted_at IS NULL
条件。我该如何删除此条件并获取所有行,包括软删除的行。
我尝试使用AllWithDeleted函数,但它适用于主模型pipeline_trigger_events(该表无论如何都没有deleted_at列),而不适用于pipeline_triggers
,因此会出现以下错误:
pg: model=PipelineTriggerEvent does not support soft deletes
英文:
I am having trouble generating correct select query with joins using go-pg
orm where one table record can be soft-deleted and other 2 cant.
DB tables:
pipeline_instances | |
---|---|
instance_id int | |
pipeline_id int | |
event_id int |
pipeline_triggers | |
---|---|
id int | |
pipeline_id int | |
deleted_at timestamp |
pipeline_trigger_events | |
---|---|
event_id int | |
trigger_id int |
go-pg Models:
type pipelineTriggerEvent struct {
tableName struct{} `pg:"pipeline_trigger_events,alias:pte"`
Trigger *pipelineTrigger `pg:"rel:has-one,join_fk:id"`
PipelineInstance *pipelineInstance `pg:"rel:has-one,join_fk:event_id"`
*TriggerEvent
}
type pipelineTrigger struct {
tableName struct{} `pg:"pipeline_triggers,alias:pt"`
*Trigger
}
type pipelineInstance struct {
tableName struct{} `pg:"pipeline_pipeline_instances,alias:ppi"`
*PipelineInstance
}
The query I am trying to generate:
SELECT
pte.*, trigger.*, pipeline_instance.*
FROM
pipeline_trigger_events AS pte
LEFT JOIN pipeline_triggers AS trigger ON (trigger.id = pte.trigger_id)
LEFT JOIN pipeline_pipeline_instances AS pipeline_instance ON pipeline_instance.event_id = pte.event_id AND trigger.pipeline_id = pipeline_instance.pipeline_id
The query getting generated by go-pg orm:
SELECT
pte.*, trigger.*, pipeline_instance.*
FROM
pipeline_trigger_events AS pte
LEFT JOIN pipeline_triggers AS trigger ON (trigger.id = pte.trigger_id)
AND trigger.deleted_at IS NULL -- this is the unwanted line.
LEFT JOIN pipeline_pipeline_instances AS pipeline_instance ON pipeline_instance.event_id = pte.event_id AND trigger.pipeline_id = pipeline_instance.pipeline_id
var triggerevents []pipelineTriggerEvent
q := db.Model(&triggerevents).
Column("pte.*").
Relation("Trigger").
Relation("PipelineInstance", func(q *orm.Query) (*orm.Query, error) {
q = q.Join(" AND trigger.pipeline_id = pipeline_instance.pipeline_id")
return q, nil
})
Of all the 3 tables/models mentioned above, only pipeline_triggers table has deleted_at
column that is used for soft deletion. My requirement is to include the soft deleted pipeline_triggers rows also in the result set. But go-pg
orm is automatically adding the trigger.deleted_at IS NULL
condition in the join
clause. How can I remove this condition and get all rows including soft deleted ones.
I tried using AllWithDeleted function but it works on the main model, which is pipeline_trigger_events (and this table does not have deleted_at column anyway) and not on pipeline_triggers
and therefore fails with this error:
pg: model=PipelineTriggerEvent does not support soft deletes
答案1
得分: 1
在浏览了一下 pg-go 的代码后,我不确定你想要做的是否受支持。要确定,你可能需要在调试器中逐步执行下面的代码。
在构建连接查询的查询时,有这部分代码:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L283
if isSoftDelete {
b = append(b, " AND "...)
b = j.appendAlias(b)
b = j.appendSoftDelete(b, q.flags)
}
第一行 j.appendAlias(b)
调用了下面的 appendAlias()
函数:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L200
func appendAlias(b []byte, j *join) []byte {
if j.hasParent() {
b = appendAlias(b, j.Parent)
b = append(b, "__"...)
}
b = append(b, j.Rel.Field.SQLName...)
return b
}
由于这两个连接查询都有一个 has-one 的父关系,它会被添加到所有的表中:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L153
func (j *join) hasParent() bool {
if j.Parent != nil {
switch j.Parent.Rel.Type {
case HasOneRelation, BelongsToRelation:
return true
}
}
return false
}
我认为对你有用的解决方法是只为父关系调用 appendAlias()
,而不是其他两个关系,但看起来 pg-go 并不支持这样做。
对于这个问题,你可以直接调用 pg.Query()
或 pg.QueryWithContext()
,并传入你上面提到的 SQL 语句。
值得一提的是,pg-go/pg 处于维护模式,所以他们可能不会支持这个功能。根据你的项目对 pg-go 的依赖程度,你可以考虑使用正在积极开发的 Bun。
附录
这是在上面的第一个代码片段中调用的 appendSoftDelete()
函数:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L189
func (j *join) appendSoftDelete(b []byte, flags queryFlag) []byte {
b = append(b, '.')
b = append(b, j.JoinModel.Table().SoftDeleteField.Column...)
if hasFlag(flags, deletedFlag) {
b = append(b, " IS NOT NULL"...)
} else {
b = append(b, " IS NULL"...)
}
return b
}
英文:
After looking through the code of pg-go a bit, I don’t know if what you’re trying to do is supported. To know for sure you’d probably want to step through the code below in a debugger.
When the query is being built for the joins, it has this section:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L283
if isSoftDelete {
b = append(b, " AND "...)
b = j.appendAlias(b)
b = j.appendSoftDelete(b, q.flags)
}
The line j.appendAlias(b)
calls the appendAlias()
function below:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L200
func appendAlias(b []byte, j *join) []byte {
if j.hasParent() {
b = appendAlias(b, j.Parent)
b = append(b, "__"...)
}
b = append(b, j.Rel.Field.SQLName...)
return b
}
Since the joins both have a has-one parent relation, it gets added for all the tables:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L153
func (j *join) hasParent() bool {
if j.Parent != nil {
switch j.Parent.Rel.Type {
case HasOneRelation, BelongsToRelation:
return true
}
}
return false
}
I think what would fix this for you would be to only call appendAlias()
for the parent relation and not the other two, but it doesn’t look like that’s supported by pg-go.
What you can do for this is just call pg.Query()
or pg.QueryWithContext()
and pass in the sql statement you included above.
It’s also worth mentioning that pg-go/pg is in maintenance mode so it’s unlikely they’ll ever support this. Depending on how entrenched this project is in pg-go, you might consider using Bun which is actively being developed.
Appendix
Here’s the appendSoftDelete()
function that gets called in the first snippet above:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L189
func (j *join) appendSoftDelete(b []byte, flags queryFlag) []byte {
b = append(b, '.')
b = append(b, j.JoinModel.Table().SoftDeleteField.Column...)
if hasFlag(flags, deletedFlag) {
b = append(b, " IS NOT NULL"...)
} else {
b = append(b, " IS NULL"...)
}
return b
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论