我希望编写最简单,最有效的SQL查询来检索与给定events
相关的所有user
。
这是我的架构的简单表示形式:
有几点需要注意:
users
通过teams
属于memberships
。teams
可以包含多个collections
,apps
和webhooks
。collections
也可以包含多个webhooks
。webhooks
可以属于team
或collection
,但只能属于一个。events
可以属于任何对象,但只能属于一个。这似乎是大多数SaaS类型公司所拥有的相当基本的设置(例如Slack或Stripe)。一切都是"拥有"由团队组成,但用户属于团队并与界面进行交互。
鉴于该设置,我想创建一个解决...
的SQL查询通过
id
查找与给定用户相关(直接或间接)的所有事件。
我可以轻松编写直接或间接通过特定方式查找的查询。例如......
通过
id
查找与用户直接相关的所有事件。
SELECT *
FROM events
WHERE user_id = ${id}
或者...
通过他们的团队查找与用户间接相关的所有事件。
SELECT events.*
FROM events
JOIN memberships ON memberships.team_id = events.team_id
WHERE memberships.user_id = ${id}
甚至......
通过其团队的任何馆藏查找与用户间接相关的所有事件。
SELECT events.*
FROM events
JOIN collections ON collections.id = events.collection_id
JOIN memberships ON memberships.team_id = collections.team_id
WHERE memberships.user_id = ${id}
Webhooks变得更复杂,因为它们可以通过两种不同的方式相关联......
通过其团队或馆藏的任何网络链接,查找与用户间接相关的所有事件。
SELECT *
FROM events
WHERE webhook_id IN (
SELECT webhooks.id
FROM webhooks
JOIN memberships ON memberships.team_id = webhooks.team_id
WHERE memberships.user_id = ${id}
)
OR webhook_id IN (
SELECT webhooks.id
FROM webhooks
JOIN collections ON collections.id = webhooks.collection_id
JOIN memberships ON memberships.team_id = collections.team_id
WHERE memberships.user_id = ${id}
)
但正如您所看到的,通过所有这些路径,用户可以通过许多不同的方式与发生的事件相关联!因此,当我尝试成功获取所有相关事件的查询时,它最终看起来像......
SELECT *
FROM events
WHERE user_id = ${id}
OR app_id IN (
SELECT apps.id
FROM apps
JOIN memberships ON memberships.team_id = apps.team_id
WHERE memberships.user_id = ${id}
)
OR collection_id IN (
SELECT collections.id
FROM collections
JOIN memberships ON memberships.team_id = collections.team_id
WHERE memberships.user_id = ${id}
)
OR memberships_id IN (
SELECT id
FROM memberships
WHERE user_id = ${id}
)
OR team_id IN (
SELECT team_id
FROM memberships
WHERE user_id = ${id}
)
OR webhook_id IN (
SELECT webhooks.id
FROM webhooks
JOIN memberships ON memberships.team_id = webhooks.team_id
WHERE memberships.user_id = ${id}
)
OR webhook_id IN (
SELECT webhooks.id
FROM webhooks
JOIN collections ON collections.id = webhooks.collection_id
JOIN memberships ON memberships.team_id = collections.team_id
WHERE memberships.user_id = ${id}
)
答案 0 :(得分:6)
与任何查询一样,最有效的方法是"它取决于"。有很多变量在起作用 - 表中的行数,行长度,是否存在索引,服务器上的RAM等等。
我能想到处理这类问题的最佳方法(思考可维护性和效率的braod方法)是通过使用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)
;
这样做的好处是:
答案 1 :(得分:4)
我能想到的唯一能让它变得更快的就是使用工会。
SELECT e.*
FROM events e
WHERE user_id = ${id}
UNION
select e.*
FROM apps a
join events e on a.apps_id = e.apps_id
JOIN memberships ON memberships.team_id = apps.team_id
WHERE memberships.user_id = ${id}
UNION
select e.*
from
FROM collections c
join events e on e.collections_id = c.collections_id
JOIN memberships ON memberships.team_id = collections.team_id
WHERE memberships.user_id = ${id}
UNION
select e.*
FROM memberships m
join events e on e.memberships_id = e.memberships_id
WHERE user_id = ${id}
UNION
...;
答案 2 :(得分:3)
我不知道您对架构有多少控制权。如果答案是"没有"然后不再阅读。我不打算在这里放一些细节,以防它不适合你的情况,但它看起来像是我的所有权模型。
即
<强> BaseTable 强>
编号
IdOwner(基础表上的FK到Id - 非常重要)
键入(User = 0,App = 1,Collection = 2等,或使用枚举)
应用强>
Id(FK到BaseTable)
<强>集合强>
Id(FK到BaseTable)
<强>成员强>
Id(FK到BaseTable)
<强>网络挂接强>
Id(FK到BaseTable)
<强>团队强>
Id(FK到BaseTable)
<强>事件强>
Id(FK到BaseTable)
<强>成员强>
Team_Id(FK到Basetable或Team)
User_Id(FK到Basetable或用户)
用户强>
Id(FK到BaseTable)
然后您的查询成为递归CTE: &#34;找到我所拥有的所有类型的对象 - 或者最终由用户x&#34;
拥有这会为您提供一个ID列表,然后您必须加入“事件”表并获得对象。
这种模型确实有点毛茸茸,因为加载你必须加入基表的任何东西,但对于这种嵌套的所有权,它的效果非常好。
我想将此作为评论发布,但如果我这样做,格式化将会消失,因此我将其作为答案发布。如果它有帮助,你想要更多细节随时回复我。
如果我完全错过了这一点并且这并没有帮助,请不要对我大喊大叫(之前有过这样的话)只是说'#34;谢谢,亚当,但那并没有#39; t help&#34;我会删除它。
亲切的问候,
亚当。