How do I best design a database where I have one table of players (with primary key player_id) which I want to pair into teams of two so that the database can enforce the constraint that each team consists of exactly two players and each player is in at most one team?
I can think of two solutions, but both I'm not too happy about.
One possibility is to have two columns player1_id and player2_id that are unique foreign keys pointing to the player_id column in the player table. An additional check is needed so that no player is at the same time player1 of one team and player2 of a second team.
The other possibility that comes to my mind is to connect the player table and the team table with a team membership table that has a unique foreign key to the player_id column in the player table and a second foreign key pointing to the primary key of the team table. Here a check has to be added that every team has exactly two members.
Is there a better design that simplifies the checking of the constraints?
If it matters: the database I'm using is PostgreSQL 8.4 and I prefer its powerful rule system to triggers wherever possible.
EDIT: A solution based on the answer of AlexKuznetsov
It doesn't feel perfect to me yet, but I like it much better than what I had before. I modified Alex' solution since I don't want to have a foreign key from players to teams, as there is an application phase where players can enroll.
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)
);
This definition makes sure that team memberships come in couples (and will be inserted pairwise). Now players can be in at most one team and teams can have exactly 0 or exactly 2 players. To ensure that each team has members I could add a foreign key in the team table that points to any of its two memberships. But like Erwin I'm not a fan of deferred constraint checking. Any ideas how to improve with respect to this? Or is there a completely different, better approach?
PS: The methods works also for teams with n>2 players. One just has to replace OtherNumberInTeam by NextNumberInTeam with the value (i.e. constraint) NumberInTeam+1 mod n.