多对多表 - 性能差

时间:2017-09-19 13:28:50

标签: postgresql indexing many-to-many postgresql-performance

提供以下表格:

--- player --
id serial
name VARCHAR(100)
birthday DATE
country VARCHAR(3)
PRIMARY KEY id

--- club ---
id SERIAL
name VARCHAR(100)
country VARCHAR(3)
PRIMARY KEY id

--- playersinclubs ---
id SERIAL
player_id INTEGER (with INDEX)
club_id INTEGER (with INDEX)
joined DATE
left DATE
PRIMARY KEY id

每个玩家在桌面播放器中都有一行(带有他的属性)。同样,每个俱乐部都有桌俱乐部的入场券。 对于他职业生涯中的每个站点,玩家都有一个表格玩家在俱乐部(n-m)中输入玩家加入的日期以及玩家离开俱乐部时的日期。

我的主要问题是这些表的性能。在桌面播放器中,我们有超过1000万条目。如果我想显示一个俱乐部的历史记录,所有球员都为这个俱乐部效力,我的选择如下:

SELECT * FROM player
 JOIN playersinclubs ON player.id = playersinclubs.player_id
 JOIN club ON club.id = playersinclubs.club_id
WHERE club.dbid = 3;

但是对于大量玩家来说,桌面播放器上的序列扫描将被执行。这种选择需要花费很多时间。

在我为我的应用程序实现一些新功能之前,每个玩家只有一个团队(只有今天的团队和玩家)。 所以我没有表球员俱乐部。相反,我在桌面播放器中有一个team_id。我可以使用where子句team_id = 3直接在桌面播放器中选择团队的玩家。

是否有人为我的数据库结构提供了一些性能提示以加快这些选择?

2 个答案:

答案 0 :(得分:1)

最重要的是,您需要playersinclubs(club_id, player_id) 上的索引。其余的是细节(可能仍然有很大的不同)  您需要准确了解自己的实际目标。你写道:

  所有球员都为这个俱乐部效力:

您根本不需要加入club

SELECT p.* 
FROM   playersinclubs pc
JOIN   player         p ON p.id = pc.player_id
WHERE  pc.club_id = 3;

您在输出中也不需要列playersinclubs,这对性能来说只是一小部分 - 除非它允许playersinclubs上的仅索引扫描,然后它可能是实质性的。

您可能不需要在结果中所有player。只有SELECT您实际需要的列。

player上的PK提供了该表所需的索引。

您需要playersinclubs(club_id, player_id)上的索引,但使其唯一,除非玩家不允许第二次加入同一个俱乐部。

如果玩家可以多次加入并且你只想要一个“所有玩家”列表,那么你还需要添加一个DISTINCT步骤来折叠重复的条目。你可以:

SELECT DISTINCT p.* ...

但是,既然你正试图优化性能:早期消除欺骗会更便宜:

SELECT p.*
FROM  (
   SELECT DISTINCT player_id
   FROM   playersinclubs
   WHERE  club_id = 3;
   ) pc
JOIN   player p ON p.id = pc.player_id;

也许您真的希望playersinclubs中的所有条目以及表格的所有列。但你的描述不然。查询和索引会有所不同。

密切相关的答案:

答案 1 :(得分:0)

表格看起来很好,查询也是如此。因此,让我们看看查询应该做什么:

  1. 选择ID为3的俱乐部。可以通过PK索引访问的一条记录。
  2. 选择俱乐部ID 3的所有球员俱乐部记录。因此我们需要一个以此专栏开头的索引。如果您没有,请创建它。
  3. 我建议:

    create unique index idx_playersinclubs on playersinclubs(club_id, player_id, joined);
    

    这将是表格中唯一的商业密钥。我知道在许多具有技术ID的数据库中没有建立这些独特的约束,但我认为这是这些数据库中的一个缺陷,并且总是会创建这些约束/索引。

    1. 使用获得的玩家ID并相应地选择玩家。我们可以从playersinclubs记录中获取玩家ID,但它也是我们索引中的第二列,因此DBMS可以选择其中一个来执行连接。 (它可能会使用索引中的列。)
    2. 所以也许只是上面的索引还不存在。