使用带有隐式联接路径的 jooq 3.19,在同一个表上生成多个联接。
这是预期的吗?
同一个表上的多个联接是否会带来性能问题?
示例:
DSL.using( SQLDialect.SQLSERVER2017, new Settings().withRenderImplicitJoinToManyType( RenderImplicitJoinType.LEFT_JOIN ) )
.select( Entity.ENTITY.ID ).from( Entity.ENTITY )
.where( Entity.ENTITY.subEntity()
.entity().subEntity()
.entity().subEntity()
.entity().subEntity()
.entity().NAME.eq( "a" ) )
.getSQL()
它生成以下sql:
"select [ENTITY].[ID] from ([ENTITY]
left outer join ([SUB_ENTITY] [alias_87004649]
left outer join ([SUB_ENTITY] [alias_112570288]
left outer join ([SUB_ENTITY] [alias_55342369]
left outer join ([SUB_ENTITY] [alias_126614199]
join [ENTITY] [alias_74449122]
on [alias_126614199].[ENTITY_ID] = [alias_74449122].[ID])
on [alias_126614199].[ENTITY_ID] = [alias_55342369].[ID])
on [alias_55342369].[ENTITY_ID] = [alias_112570288].[ID])
on [alias_112570288].[ENTITY_ID] = [alias_87004649].[ID])
on [alias_87004649].[ENTITY_ID] = [ENTITY].[ID])
where [alias_74449122].[NAME] = ?"
是的。
虽然起初,看起来来回导航相同的外键路径是没有意义的,并且可以消除连接,但实际上不能。每个对多路径段都可以与前面的段创建笛卡尔积,就像任何普通的显式对多连接可以做到这一点。因此,由于需要这种笛卡尔积的可能性很小,jOOQ 在不改变查询逻辑的情况下无法阻止它,所以如果你告诉 jOOQ 多次重新加入同一个表(即使你不打算这样做) ,那么 jOOQ 必须多次重新加入同一个表才能实现您可能想要的查询逻辑。
请注意,跳回到父级实际上已被消除,因为消除它们不会改变您的情况下的查询语义,因为没有投影父列:
当然!这不仅会产生性能问题,还会得到错误的结果。您似乎只是为了“让它发挥作用”而打开
Settings.renderImplicitJoinToManyType
,而没有考虑其含义。但其含义正是我所说的。您的 WHERE
子句现在会产生笛卡尔积,我怀疑您想要也不期望它。
意外的重复对象并不是这种隐式多对多路径连接导致的主要问题。主要问题是,放置在在您的特定情况下,您可能希望使用
SELECT
子句或WHERE
子句(以及其他子句)中的隐式对多路径将能够生成行,而实际上SELECT
仅转换行(如Stream.map()
)和WHERE
仅过滤行(如Stream.filter()
)。如果这些子句能够有效地生成行,那么 SQL 非常不惯用且令人困惑。
连接路径相关
将谓词移动到
EXISTS
子查询中,例如
ctx
.select(ENTITY.ID)
.from(ENTITY)
.where(exists(
selectOne()
.from(ENTITY.subEntity()
...
.entity().NAME.eq("a")
)
))
.getSQL()
这不需要启用上述有争议的Settings.renderImplicitJoinToManyType
。您仍然会遇到奇怪的路径和不必要的连接,但至少,您现在不会得到任何笛卡尔积。摆脱循环
知道可以删除这些循环,那么最好自己做。我希望上面的答案能够澄清为什么 jOOQ 无法自动为您执行此操作。