如何对具有3个(半)固定位置的团队进行建模,并高效地搜索相同/不同的团队?

问题描述 投票:0回答:1

我想在我的 Postgres 数据库中代表一个团队。 这些团队始终有 3 名成员。 每个成员都有一个槽位,1 为领导者,2 和 3 为成员(也可用于按良好顺序显示成员) 每个成员都有一个级别,但仅限于这个团队(一个角色可以出现在不同团队中,具有不同的级别。

不过,我的特点之一是,我想选择所有具有不同组成的团队,这意味着不同的领导者和成员。 (A、B、C 组成的队伍被视为与 A、C、B 相同,但与 C、A、B 组成的队伍不同)

在另一个功能中,我想选择包含完全相同组成的所有团队。

目前我有3张桌子,

CREATE TABLE teams (
    "teamID" integer NOT NULL,
    "remainingTime" double precision NOT NULL,
    "desc" text NOT NULL,
    difficulty character varying NOT NULL
);

CREATE TABLE public.team_member (
    "teamID" smallint NOT NULL,
    "charID" smallint NOT NULL,
    "level" smallint NOT NULL,         /*can be from 1 to 5*/
    "slot" smallint DEFAULT 1 NOT NULL /*1, 2 or 3*/
);

CREATE TABLE public."character" (
    "charID" integer NOT NULL,
    name character varying NOT NULL,
    /* others attribute */
);

我现在的问题是,编写与我的功能相对应的查询的最佳方法是什么,即一个用于不同的团队,另一个用于具有相同组成的团队?

我想我可以为每个团队创建一个代表其成员 ID 的数组,然后比较它们,但这不会导致很多团队的性能问题吗?

对于这种情况是否有更优化的模型? 我想过创建一种表,将其 3 个成员的 ID 存储在一行中,然后创建另一个表

team_variation
,存储 3 个成员的级别,以及相关的描述和时间,但我不知道这是否是真是个好主意

最后一点,我在 Javascript 服务器上使用这个数据库,这样我就可以在必要时处理查询收到的数据,该查询本来可以完成我想要的部分工作

sql postgresql database-design model
1个回答
0
投票

我提出这种关系设计:

CREATE TABLE team (
  team_id        int2 PRIMARY KEY
, remaining_time float NOT NULL  -- ?? use date/time type for temporal data!
, description    text NOT NULL   -- ? description?
, difficulty     text NOT NULL
);

CREATE TABLE person (
  pers_id   int2 PRIMARY KEY
, full_name text NOT NULL
  -- other attributes
);

CREATE TABLE team_member (
  team_id int2 NOT NULL REFERENCES team
, pers_id int2 NOT NULL REFERENCES person
, level   int2 NOT NULL CHECK (level BETWEEN 1::int2 AND 5::int2)  -- can be from 1 to 5
, slot    "char" NOT NULL DEFAULT 'l'::"char"
, pos     "char" NOT NULL DEFAULT '1'::"char"
, CONSTRAINT team_member_pkey PRIMARY KEY (team_id, pers_id)
  -- team can have 1 leader and two members, period.
, CONSTRAINT chk_1lead_2members CHECK (slot = 'l' AND pos = '1'
                                   OR slot = 'm' AND pos IN ('2', '3'))
  -- 'l' ... leader, 'm' ... member
  -- leader must have pos '1', members must have pos '2' or '3'
, CONSTRAINT uni_team_slot_pos UNIQUE(slot, pos, team_id) INCLUDE (pers_id)
  -- team must have distinct persons; plus: provides essential index
);

-- add idx with reversed cols
CREATE UNIQUE INDEX team_member_pers_id_team_id ON team_member(pers_id, team_id);

小提琴

NOT NULL
CHECK
UNIQUE
PRIMARY KEY
FOREIGN KEY
约束》的组合可强制执行您的模型。您仍然可以进入不完整的团队(并非所有槽位都已满),从而允许中间状态。但这些都被排除在我下面的查询中。

team_member
混合使用约束和索引是有充分理由的。参见:

我保留了你对

smallint
的选择,但使
team
person
的PK匹配。不要在这里引入类型不匹配。我什至将
team_member
行的最小有效负载大小保持在 8 个字节(也许这就是您优化的目的?)但是除非您有 充分的理由,并且可以可靠地 排除溢出,否则请使用
integer
(或甚至
bigint
)。不易出错。仅增加很少的成本。

请注意数据类型

"char"
,以便进行廉价枚举。 (可选。)不要与
char
varchar
混淆!参见:

对日期/时间数据使用日期/时间数据类型(第

remaining_time
列)。不是
float

没有充分理由,不要混合使用单数和复数形式的表名称。

使用合法的、小写的、不带引号的标识符。特别要避免保留字。参见:

查询

已经对性能进行了一定程度的优化。

各具特色的团队

不同团队的完整列表。从一组受骗者中选择 ID 最小的团队。排除不完整的团队。

SELECT DISTINCT ON (l.pers_id, m.m1, m.m2)
       team_id, l.pers_id AS lead, m.m1, m.m2
FROM   team_member l
JOIN  (
   SELECT team_id, min(pers_id) AS m1, max(pers_id) AS m2
   FROM   team_member m
   WHERE  m.slot = 'm'
   GROUP  BY 1
   HAVING count(*) = 2  -- exclude incomplete teams
   ) m USING (team_id)
WHERE  l.slot = 'l'
ORDER  BY l.pers_id, m.m1, m.m2, team_id;  -- pick team with smallest ID from dupes

参见:

同样的团队

选择与给定团队相同的团队

(1, 2, 3)
-
1
为领先。再次强调,排除不完整的团队。

SELECT team_id, l.pers_id AS lead, m1.pers_id AS m1, m2.pers_id AS m2
FROM   team_member l
JOIN   team_member m1 USING (team_id)
JOIN   team_member m2 USING (team_id)
WHERE  l.slot = 'l'
AND    l.pers_id = 1  -- lead
AND    m1.slot = 'm'
AND    m1.pers_id = LEAST (2,3)  -- member 1
AND    m2.slot = 'm'
AND    m2.pers_id = GREATEST (2,3)   -- member 2
ORDER  BY lead, m1, m2;

小提琴

© www.soinside.com 2019 - 2024. All rights reserved.