Проектирование базы данных для принудительного сопряжения - PullRequest
0 голосов
/ 14 июля 2009

Как мне лучше спроектировать базу данных, где у меня есть одна таблица игроков (с первичным ключом player_id ), которую я хочу объединить в группы по две, чтобы база данных могла применять ограничение, которое состоит из каждой команды из ровно двух игроков, и каждый игрок в не более одной команды?

Я могу придумать два решения, но оба они меня не очень устраивают.

Одна возможность состоит в том, чтобы иметь два столбца player1_id и player2_id , которые являются уникальными внешними ключами, указывающими на столбец player_id в плеере Таблица. Требуется дополнительная проверка, чтобы ни один игрок не являлся одновременно игроком 1 одной команды и игроком 2 второй команды.

Другая возможность, которая приходит мне в голову, - это соединить игровой стол и командный стол с таблицей членства в команде, которая имеет уникальный внешний ключ к столбцу player_id в стол игрока и второй внешний ключ, указывающий на первичный ключ таблицы команды. Здесь нужно добавить проверку, что в каждой команде ровно два участника.

Есть ли лучший дизайн, который упрощает проверку ограничений?

Если это имеет значение: я использую базу данных PostgreSQL 8.4, и я предпочитаю ее мощную систему правил триггерам, где это возможно.

РЕДАКТИРОВАТЬ: решение на основе ответа Алексея Кузнецова

Мне это пока не кажется идеальным, но мне это нравится намного лучше, чем было раньше. Я изменил решение Алекса, так как я не хочу иметь внешний ключ от игроков к командам, так как на этапе подачи заявки игроки могут зарегистрироваться.

create table TeamMemberships(
  player_id int not null unique references Players(player_id),
  team_id int not null references Teams(team_id),
  NumberInTeam int not null check(NumberInTeam in (0,1)),
  OtherNumberInTeam int not null, -- check(OtherNumberInTeam in (0,1)) is implied
  check(NumberInTeam + OtherNumberInTeam = 1)
  foreign key (team_id, OtherNumberInTeam) references TeamMemberships(team_id, NumberInTeam),
  primary key (team_id, NumberInTeam)
);

Это определение гарантирует, что членство в команде происходит парами (и будет вставлено попарно). Теперь игроки могут состоять максимум из одной команды, а в командах может быть ровно 0 или ровно 2 игрока. Чтобы убедиться, что в каждой команде есть участники, я мог бы добавить внешний ключ в таблицу команд, который указывает на любое из двух членств. Но, как и Эрвин, я не фанат отложенной проверки ограничений. Есть идеи, как улучшить это? Или есть совершенно другой, лучший подход?

PS: метод работает также для команд с n> 2 игроками. Нужно просто заменить OtherNumberInTeam на NextNumberInTeam со значением (т.е. ограничением) NumberInTeam + 1 mod n.

Ответы [ 4 ]

1 голос
/ 15 июля 2009

Я не знаю, может ли это работать на Postgress, но вот решение SQL Server:

CREATE TABLE dbo.Teams(TeamID INT NOT NULL PRIMARY KEY);
GO
CREATE TABLE dbo.Players(PlayerID INT NOT NULL PRIMARY KEY,
  TeamID INT NOT NULL FOREIGN KEY REFERENCES dbo.Teams(TeamID),
  NumberInTeam INT NOT NULL CHECK(NumberInTeam IN (1,2)),
  TeamMateID INT NOT NULL,
  TeamMatesNumberInTeam INT NOT NULL,
-- if NumberInTeam=1 then TeamMatesNumberInTeam must be 2
-- and vise versa
  CHECK(NumberInTeam+TeamMatesNumberInTeam = 3), 
  UNIQUE(TeamID, NumberInTeam),
  UNIQUE(PlayerID, TeamID, NumberInTeam),
  FOREIGN KEY(TeamMateID, TeamID, TeamMatesNumberInTeam)
    REFERENCES dbo.Players(PlayerID, TeamID, NumberInTeam)
);

INSERT INTO dbo.Teams(TeamID) SELECT 1 UNION ALL SELECT 2;
GO

- вы можете вставлять игроков только в полные пары

INSERT INTO dbo.Players(PlayerID, TeamID, NumberInTeam, TeamMateID, TeamMatesNumberInTeam)
SELECT 1,1,1,2,2 UNION ALL
SELECT 2,1,2,1,1;

Вы можете попытаться вставить одного игрока, или удалить игрока из команды, или добавить более двух игроков на команду - все не получится из-за полного набора ограничений.

Примечание: практика в SQL Server заключается в явном именовании всех ограничений. Я не назвал свои ограничения на тот случай, если они несовместимы с Postgres.

0 голосов
/ 14 июля 2009

Первое решение ограничено, если когда-либо может потребоваться переход на команды с тремя участниками.

Мне нравится идея таблицы teamMemebrs, но для усиления ваших ограничений вы никогда не сможете вставлять только одну запись за раз. Все ваши вставки должны быть набором из двух. Кроме того, вы разбираетесь в том, что вы делаете, когда игрок A ia в команде A, и вы хотите, чтобы его перевели в команду B. Теперь вам нужно найти кого-то, кто перейдет в команду A, а затем добавить его и его партнера в команду B.

Может быть, лучше создавать и заполнять команды, но делать их активными только в том случае, если у них два члена и каждый член только в одной команде? По крайней мере, тогда вы можете вносить изменения по одному. SP, вы можете переместить Человека А из Команды А в Команду Б и сделать Команду А неактивной, пока не найдете другого человека, который может добавить его.

0 голосов
/ 15 июля 2009

Мне кажется, что с дизайном player1-id player2_id вы можете проверить все свои ограничения после одной вставки в командный стол.

В проекте "команда + членство в команде" вы попадаете в уродство отложенной проверки ограничений, помимо всего того, что нужно настроить для запуска / правила.

Между прочим: такие системы, как SIRA_PRISE, создаются с явной целью решения именно таких проблем принудительного применения ограничений исключительно декларативно, т. Е. Без каких-либо проблем с триггерами или любой другой формы программирования. Вы можете быть заинтересованы.

0 голосов
/ 14 июля 2009

Это звучит как нечто, что было бы намного проще реализовать в пользовательском интерфейсе, чем в базе данных.

При добавлении игроков в команды не допускайте добавления более двух игроков, и допускайте добавление только тех игроков, которых нет в команде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...