Както при всяко запитване, най-ефективният метод е „зависи“. Има много променливи в игра - броя на редовете в таблиците, дължините на редовете, дали съществуват индекси, RAM на сървъра и т.н. и т.н.
Най-добрият начин, по който мога да се сетя за справяне с този вид проблем (мислейки за поддържаемост и смел подход към ефективността), е чрез използване на CTE, което ви позволява да създадете временен резултат и да използвате повторно този резултат във вашата заявка. CTE използват ключовата дума WITH и по същество псевдоним на резултат като таблица, така че да можете да се ПРИСЪЕДИНИТЕ към нея няколко пъти:
WITH user_memberships AS (
SELECT *
FROM memberships
WHERE user_id = ${id}
), user_apps AS (
SELECT *
FROM apps
INNER JOIN user_memberships
ON user_memberships.team_id = apps.team_id
), user_collections AS (
SELECT *
FROM collections
INNER JOIN user_memberships
ON user_memberships.team_id = collections.team_id
), user_webhooks AS (
SELECT *
FROM webhooks
LEFT OUTER JOIN user_collections ON user_collections.id = webhooks.collection_id
INNER JOIN user_memberships
ON user_memberships.team_id = webhooks.team_id
OR user_memberships.team_id = user_collections.team_id
)
SELECT events.*
FROM events
WHERE app_id IN (SELECT id FROM user_apps)
OR collection_id IN (SELECT id FROM user_collections)
OR membership_id IN (SELECT id FROM user_memberships)
OR team_id IN (SELECT team_id FROM user_memberships)
OR user_id = ${id}
OR webhook_id IN (SELECT id FROM user_webhooks)
;
Ползите от това да го правите по този начин са:
- Всеки CTE може да се възползва от индекс на съответните JOIN предикати и да върне резултати само за това подмножество по-бързо, вместо плановият инструмент за изпълнение да се опитва да разрешава поредица от сложни предикати
- CTE могат да се поддържат индивидуално, което улеснява отстраняването на проблеми с подгрупи
- Вие не нарушавате принципа DRY
- Ако CTE има стойност извън заявката, можете да го преместите в съхранена процедура и да я препратите вместо това