Som med alla frågor är den mest effektiva metoden "det beror på". Det finns många variabler i spel - antalet rader i tabeller, radlängder, om det finns index, RAM-minnet på servern, etc etc.
Det bästa sättet jag kan tänka mig att hantera den här typen av problem (tänker på underhållsbarhet och ett bra förhållningssätt till effektivitet) är att använda CTE, som låter dig skapa ett tillfälligt resultat och återanvända det resultatet genom hela din fråga. CTE:er använder nyckelordet WITH, och i huvudsak alias ett resultat som en tabell, så att du kan GÅ MED mot det flera gånger:
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)
;
Fördelarna med att göra det på detta sätt är:
- Varje CTE kan dra fördel av ett index på lämpliga JOIN-predikat och returnera resultat för just den delmängden snabbare, snarare än att exekveringsplaneraren försöker lösa en serie komplexa predikat
- CTE:erna kan underhållas individuellt, vilket gör det enklare att felsöka problem med delmängder
- Du bryter inte mot DRY-principen
- Om CTE har värde utanför frågan kan du flytta den till en lagrad procedur och referera till den istället