Используя 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] = ?"
Привет спасибо. мы пытаемся создать динамический запрос, переведенный с языка rql. но пользователь может писать циклические вещи: "filter=eq(subEntities.subEntities2.entity.name, \"a\")"
мы преобразуем его без методов пути jooq ...выбираем отдельный "ENTITY"."ID" из "ENTITY" левое внешнее соединение "SUB_ENTITY" на "SUB_ENTITY"."ENTITY_ID" = "ENTITY"."ID" левое внешнее соединение "SUB_ENTITY2" " на "SUB_ENTITY2"."SUB_ENTITY_ID" = "SUB_ENTITY"."ID" левое внешнее соединение "SUB_ENTITY2" на "SUB_ENTITY2"."ENTITY_ID" = "ENTITY"."ID" где "ENTITY"."NAME" = ? и каким-то образом мы ожидали, что использование этих методов неявного пути соединения jooq будет проще. но мы не знаем точно, влияет ли способ выполнения jooq на производительность или jooq может устранить эти дубликаты.
Да, это.
Хотя на первый взгляд кажется, что перемещение по одному и тому же пути внешнего ключа снова и снова бессмысленно, и соединения можно устранить, на самом деле это невозможно. Каждый сегмент пути «ко-многим» может создавать декартово произведение с предыдущими сегментами, точно так же, как это может сделать любое обычное явное соединение «ко-многим». Следовательно, из-за небольшой вероятности того, что такое декартово произведение будет желательным, 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
. У вас все еще будет странный путь и ненужные соединения, но, по крайней мере, теперь вы не получите никаких декартовых произведений.
Вы лучше всех знаете свою собственную семантику перевода RQL в jOOQ. Если вы знаете, что можете удалить эти циклы, то лучше всего сделайте это самостоятельно. Я надеюсь, что приведенный выше ответ прояснил, почему jOOQ не может сделать это за вас автоматически.
Почему этого нельзя было ожидать? Что ты на самом деле пытаешься здесь сделать?