我有一张桌子lineup
。
CREATE TABLE IF NOT EXISTS lineup (
match_id INTEGER REFERENCES matches,
pos_1 INTEGER REFERENCES players,
pos_2 INTEGER REFERENCES players,
pos_3 INTEGER REFERENCES players,
pos_4 INTEGER REFERENCES players,
pos_5 INTEGER REFERENCES players,
pos_6 INTEGER REFERENCES players
);
用于存储排球比赛的阵容。您在球场上有六个职位。每个职位必须有一名球员。但是,所有玩家ID都必须不同。玩家不能同时在多个位置上。如何使用约束对此建模?
match_id, pos_1, pos_2, pos_3, pos_4, pos_5, pos_6
1, 10, 11, 12, 13, 14, 15 // ok
1, 10, 10, 12, 13, 14, 15 // not ok, pos_1 == pos_2
1, 10, 11, 12, 13, 14, 10 // not ok, pos_1 == pos_6
1, 10, 11, 12, 13, 14, 14 // not ok, pos_5 == pos_6
我想到了类似的东西
CONSTRAINT no_duplicate_players CHECK (pos_1 != pos_2 AND pos_1 != pos_3 ... AND pos_5 != pos_6)
那将是一个很长的约束,我想知道是否有更简单的事情。
谢谢, 米尔科
答案 0 :(得分:3)
有效执行此操作的一种方法是通过对位置列进行标准化来进一步对表格进行标准化。
CREATE TABLE IF NOT EXISTS lineup (
match_id INTEGER NOT NULL REFERENCES matches,
position_id INTEGER NOT NULL REFERENCES positions,
player_id INTEGER NOT NULL REFERENCES players
CONSTRAINT lineups_pk PRIMARY KEY (match_id, position_id, player_id)
);
您还需要一个职位表,例如:
CREATE TABLE IF NOT EXISTS positions (
position_id INTEGER,
position TEXT, -- or whatever your character type of choice is
CONSTRAINT positions_pk PRIMARY KEY (position_id)
);
这种方法的优点是可以强制执行您要强制执行的约束,如果您确定需要添加有关位置,球员或比赛的更多信息,则将来可以灵活使用。 / p>
然后您可以通过执行以下操作以所需的格式显示数据:
SELECT
match_id
, a.player_id AS pos_1
, b.player_id AS pos_2
, c.player_id AS pos_3
, d.player_id AS pos_4
, e.player_id AS pos_5
, f.player_id AS pos_6
FROM (SELECT match_id, player_id FROM lineup WHERE position_id = 1) a
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 2) b USING (match_id)
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 3) c USING (match_id)
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 4) d USING (match_id)
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 5) e USING (match_id)
LEFT JOIN (SELECT match_id, player_id FROM lineup WHERE position_id = 6) f USING (match_id);
以这种方式设置它需要做更多的工作,但是一旦获得查询,就可以将其创建为视图,然后总会得到它,并且还从附加的规范化中受益。这种方法还使回答“ player_id 1进行了多少场比赛?”之类的问题变得更加容易。和“他们总是在同一位置玩吗?”
最初,此答案未满足我们必须确保所有六个职位都得到填补的要求。不添加任何内容,这可以确保每个已枚举位置均已填补,但可以避免插入某个位置。有两种方法可以解决此问题:
确保通过应用程序/插入技术插入
确保通过数据库触发器插入/删除
答案 1 :(得分:1)
要缩短查询长度,请使用NOT IN
CONSTRAINT no_duplicate_players
CHECK (pos_1 NOT IN(pos_2,pos_3,pos_4,pos_5)) -- n --similar for each column
或者更好的方法是将每个pos1..n
都使用外键(类似于数据仓库模式中的事实表)
这样它们将引用您的主表。
答案 2 :(得分:1)
Impaler的想法正确,但您也想保证所有六个位置都有球员。为此,您还需要NOT NULL
约束:
CREATE TABLE IF NOT EXISTS lineup (
match_id INTEGER REFERENCES matches,
pos_1 INTEGER NOT NULL REFERENCES players,
pos_2 INTEGER NOT NULL REFERENCES players,
pos_3 INTEGER NOT NULL REFERENCES players,
pos_4 INTEGER NOT NULL REFERENCES players,
pos_5 INTEGER NOT NULL REFERENCES players,
pos_6 INTEGER NOT NULL REFERENCES players,
constraint chk_linup_pos
check (pos_2 not in (pos_1) and
pos_3 not in (pos_1, pos_2) and
pos_4 not in (pos_1, pos_2, pos_3) and
pos_5 not in (pos_1, pos_2, pos_3, pos_4) and
pos_6 not in (pos_1, pos_2, pos_3, pos_4, pos_5)
);
);
尽管您可以规范化数据结构,但是当值跨越多行时,确实很难固定约束,例如“必须填充所有六个位置”。如果您真的想实现这一点,那么这种结构可能是最简单的机制。
答案 3 :(得分:0)
一个简单的约束即可:
CREATE TABLE IF NOT EXISTS lineup (
match_id INTEGER REFERENCES matches,
pos_1 INTEGER REFERENCES players,
pos_2 INTEGER REFERENCES players,
pos_3 INTEGER REFERENCES players,
pos_4 INTEGER REFERENCES players,
pos_5 INTEGER REFERENCES players,
pos_6 INTEGER REFERENCES players,
constraint chk1
check (pos_1 <> pos_2
and pos_1 <> pos_3
and pos_1 <> pos_4
and pos_1 <> pos_5
and pos_1 <> pos_6
and pos_2 <> pos_3
and pos_2 <> pos_4
and pos_2 <> pos_5
and pos_2 <> pos_6
and pos_3 <> pos_4
and pos_3 <> pos_5
and pos_3 <> pos_6
and pos_4 <> pos_5
and pos_4 <> pos_6
and pos_5 <> pos_6
)
);