Sql 获取多对多关系的有效方法

Sql 获取多对多关系的有效方法,sql,postgresql,join,many-to-many,Sql,Postgresql,Join,Many To Many,我有以下表格: team: identifier, name fan: identifier, name team_fan: team_identifier, fan_identifier 换句话说,球队和球迷之间存在着一种多对多的关系 我想把满足一定条件的所有团队都召集起来;对于每一支被选中的球队,我都想吸引所有的球迷。因此,在我的应用程序中,我希望具有以下数据结构: Team A Fan F1 Fan F2 Team B Fan F1

我有以下表格:

team:     identifier, name
fan:      identifier, name
team_fan: team_identifier, fan_identifier
换句话说,球队和球迷之间存在着一种多对多的关系

我想把满足一定条件的所有团队都召集起来;对于每一支被选中的球队,我都想吸引所有的球迷。因此,在我的应用程序中,我希望具有以下数据结构:

Team A
     Fan F1
     Fan F2
Team B
     Fan F1
     Fan F3
Team C
     Fan F2
     Fan F3
     Fan F4
我已经提出了以下解决方案:

[0]默认,典型方法

默认的典型方法是内部联接:

select     team.name, fan.name
from       team
inner join team_fan
on         team.identifier = team_fan.team_identifier
inner join fan
on         team_fan.fan_identifier = fan.identifier
where      ... (team conditions)
这提供了构建上述数据结构所需的所有信息

有很多球队和球迷可以属于多个球队。上面的查询可能不是一个好主意,因为结果中的团队和球迷是重复的。所有这些复制品都需要通过电线传输

在下面的备选方案中,我正在应用程序中进行连接。下面的备选方案可能会慢一些,但我还不知道。我想从中比较和学习

[1]非常幼稚的方法

首先,我们选择所有团队:

select name from team where ...
select name from team where ...
然后,对于标识符为X的每个团队,我们选择其球迷:

select name
from   fan
where  exists(select 1 from team_fan where team_identifier = X)
这是一个糟糕的解决方案,因为所需的查询数是
1+团队数
。此外,属于多个团队的风扇会被多次提取。我们可以做得更好

[2]自上而下的方法

首先,我们选择所有团队。在这样做的同时,我们还收集了一组属于团队的所有球迷:

select  name, array(select identifier
                from   fan
                where  exists(select 1 from team_fan where fan.identifier = team_fan.fan_identifier and team.identifier = team_fan.team_identifier)) as fans
from  team
where ...
然后,在我们的应用程序中,我们构造所有风扇标识符的并集。给定这组风扇标识符,我们可以选择所有风扇:

select name from fan where identifier in(...)
select name, array(select team_fan.team_identifier from team_fan where fan_identifier = fan.identifier and team_identifier in(...))
from   fan
where  exists(select 1 from team_fan where fan_identifier = fan.identifier and team_identifier in(...));
现在,我有足够的信息在我的应用程序中复制连接,并按照上面的演示构建数据结构

这似乎是一个更好的解决方案。查询数始终为2。而且,每个队和每个球迷只被抓到一次

[3]自下而上的方法

我调用了上一个解决方案
top-down
,因为我们正在向父(团队)添加子(fan)数组。在这种方法中,我们做的是相反的:我们向子(fan)添加一个父(团队)数组

因此,首先,让我们选择所有团队:

select name from team where ...
select name from team where ...
接下来,在我们的应用程序中,我们构造所有团队标识符的并集。根据这组球队标识符,我们可以选择所有球迷:

select name from fan where identifier in(...)
select name, array(select team_fan.team_identifier from team_fan where fan_identifier = fan.identifier and team_identifier in(...))
from   fan
where  exists(select 1 from team_fan where fan_identifier = fan.identifier and team_identifier in(...));
现在,我有足够的信息在我的应用程序中复制连接,并按照上面的演示构建数据结构

这似乎也是一个有效的解决方案。同样在这种情况下,查询数始终为2。而且,每个队和每个球迷只被抓到一次

我的问题

那么,回到我的问题上来:我想把满足某个条件的所有团队集合起来;对于每一支被选中的球队,我都想吸引所有的球迷

目前,我不确定方法2是否优于方法3(反之亦然),甚至不确定是否有更好的方法。欢迎有任何见解。

做一个简单的加入

Select
    t.identitfier team_identifier
    ,t.name team_name
    ,f.identitfier fan_identifier
    ,f.name fan_name
From team t 
inner join team_fan tf 
on t.identifier=tf.team_identifier
/* and --(team condition can be put here) */
inner join fan f on tf.fan_identifier=f.identifier
/*where ... --(or team condition can be put here)*/
做一个简单的连接

Select
    t.identitfier team_identifier
    ,t.name team_name
    ,f.identitfier fan_identifier
    ,f.name fan_name
From team t 
inner join team_fan tf 
on t.identifier=tf.team_identifier
/* and --(team condition can be put here) */
inner join fan f on tf.fan_identifier=f.identifier
/*where ... --(or team condition can be put here)*/

我建议修改选项2并完全删除风扇表

假设团队少于粉丝,这种方法将向应用程序返回更少的行,并且可能更有效,因为数组函数不需要像其他方法那样在尽可能多的行上执行

SELECT
    name, 
    array(
        SELECT DISTINCT 
            fan_identifier 
        FROM team_fan 
        WHERE team.identifier = team_fan.team_identifier
    ) as fans
FROM team
WHERE ...

我建议修改选项2并完全删除风扇表

假设团队少于粉丝,这种方法将向应用程序返回更少的行,并且可能更有效,因为数组函数不需要像其他方法那样在尽可能多的行上执行

SELECT
    name, 
    array(
        SELECT DISTINCT 
            fan_identifier 
        FROM team_fan 
        WHERE team.identifier = team_fan.team_identifier
    ) as fans
FROM team
WHERE ...

你所有的方法似乎都太复杂了。此外,选择*似乎也没有必要。你考虑过简单的内部连接吗?@DanBracuk我正在寻找简单内部连接的替代方案,以避免输出中团队和粉丝的重复。由于这些重复,结果变得巨大。备选方案可能会慢一些,但我还不知道。我想从中比较和学习。你的预期产出是什么?我看不出如何解决这个问题,因为Jout既有双面团队,也有粉丝?如何使用结果?如果它是您担心的大小,则筛选团队,并获得筛选的团队表、团队上筛选的团队\球迷表和筛选的团队\球迷表上筛选的球迷表。因此,基本上可以获得团队筛选的数据的完整副本。然后,您的应用程序可以复制SQL中的连接以分解我的应用程序中的数据,我想要一个团队列表,对于每个团队,我想要一个粉丝子列表。这是我的预期产出。我没有一个或多个SQL查询的预期输出。因此,这不是典型的
我想要一个带有输出ABC
问题的查询。关于您的建议(
然后过滤团队…
),这正是我在这些方法中所做的。我的问题是:哪一个最好?有更好的方法吗?你所有的方法看起来都太复杂了。此外,选择*似乎也没有必要。你考虑过简单的内部连接吗?@DanBracuk我正在寻找简单内部连接的替代方案,以避免输出中团队和粉丝的重复。由于这些重复,结果变得巨大。备选方案可能会慢一些,但我还不知道。我想从中比较和学习。你的预期产出是什么?我看不出如何解决这个问题,因为Jout既有双面团队,也有粉丝?如何使用结果?如果它是您担心的大小,则筛选团队,并获得筛选的团队表、团队上筛选的团队\球迷表和筛选的团队\球迷表上筛选的球迷表。因此,基本上可以获得团队筛选的数据的完整副本。然后,您的应用程序可以复制SQL中的连接以分解我的应用程序中的数据,我想要一个团队列表,对于每个团队,我想要一个粉丝子列表。这是我的预期产出。我没有预期的结果