referencing part of the composite primary key

余生颓废 提交于 2019-12-11 11:07:46

问题


I have problems with setting the reference on database table. I have following structure:

CREATE TABLE club(
    id INTEGER NOT NULL,
    name_short VARCHAR(30),
    name_full VARCHAR(70) NOT NULL
);
CREATE UNIQUE INDEX club_uix ON club(id);
ALTER TABLE club ADD CONSTRAINT club_pk PRIMARY KEY (id);

CREATE TABLE team(
    id INTEGER NOT NULL,
    club_id INTEGER NOT NULL,
    team_name VARCHAR(30)
);
CREATE UNIQUE INDEX team_uix ON team(id, club_id);
ALTER TABLE team ADD CONSTRAINT team_pk PRIMARY KEY (id, club_id);
ALTER TABLE team ADD FOREIGN KEY (club_id) REFERENCES club(id);

CREATE TABLE person(
    id INTEGER NOT NULL,
    first_name VARCHAR(20),
    last_name VARCHAR(20) NOT NULL
);
CREATE UNIQUE INDEX person_uix ON person(id);
ALTER TABLE person ADD PRIMARY KEY (id);

CREATE TABLE contract(
    person_id INTEGER NOT NULL,
    club_id INTEGER NOT NULL,
    wage INTEGER
);
CREATE UNIQUE INDEX contract_uix on contract(person_id);
ALTER TABLE contract ADD CONSTRAINT contract_pk PRIMARY KEY (person_id);
ALTER TABLE contract ADD FOREIGN KEY (club_id) REFERENCES club(id);
ALTER TABLE contract ADD FOREIGN KEY (person_id) REFERENCES person(id);

CREATE TABLE player(
    person_id INTEGER NOT NULL,
    team_id INTEGER,
    height SMALLINT,
    weight SMALLINT

);
CREATE UNIQUE INDEX player_uix on player(person_id);
ALTER TABLE player ADD CONSTRAINT player_pk PRIMARY KEY (person_id);
ALTER TABLE player ADD FOREIGN KEY (person_id) REFERENCES person(id);
-- ALTER TABLE player ADD FOREIGN KEY (team_id) REFERENCES team(id); --this is not working

It gives me this error:

Error code -5529, SQL state 42529: a UNIQUE constraint does not exist on referenced columns: TEAM in statement [ALTER TABLE player ADD FOREIGN KEY (team_id) REFERENCES team(id)]

As you can see, team table has composite primary key (club_id + id), the person references club through contract. Person has some common attributes for player and other staff types.

One club can have multiple teams. Employed person has to have a contract with a club. Player (is the specification of person) - if emplyed - can be assigned to one of the club's teams.

Is there better way to design my structure? I thought about excluding the club_id from team's primary key, but I would like to know if this is the only way. Thanks.

UPDATE 1

I would like to have the id as team identification only within the club, so multiple teams can have equal id as long as they belong to different clubs. Is it possible?

UPDATE 2 updated the naming convention as adviced by philip

Some business rules to better understand the structure:

  • One club can have 1..n teams (Main squad, Reserve squad, Youth squad or Team A, Team B... only team can play match, not club)
  • One team belongs to one club only
  • A player is type of person (other types (staff) are scouts, coaches etc so they do not need to belong to specific team, just to the club, if employed)
  • Person can have 0..1 contract with 1 club (that means he is employed or unemployed)
  • Player (if employed) belongs to one team of the club

Now thinking about it - moving team_id from player to contract would solve my problem, and it could hold the condition "Player (if employed) belongs to one team of the club", but it would be redundant for other staff types. What do you think?


回答1:


When a subrow in one table has to be a subrow in another (referenced) table, that is an inclusion dependency (IND). Eg player team_id referencing team id (not a key in team). When there is an IND and the referenced subrow is a key, that is a foreign key (FK). Eg player person_id referencing person id (a key in person). In SQL a FOREIGN KEY declaration says that there is an IND and the referenced columns are unique. (Ie declared by either PRIMARY KEY or UNIQUE). (So it actually means "foreign superkey".) In SQL we would ideally declare an IND (when there isn't also a FK) by a CHECK constraint. (But DBMSes do not support CHECK well). Ie check for player that team_id is in team projected on id. But you tried to declare an SQL FK. It fails because, as the error message says, team id is not unique.


(Notice here the distinction commented between teams and their team ids. Because a team id doesn't identify a team, you should only speak of a team identified by a team id and club. OOP must distinguish not only between teams in the world and team ids (some kind of string) in the world but also "team_id" pointer/reference values of a programming language. It was commented that id" is a bad name because a team id alone doesn't identify a team. We could just use the term used in the world.)

But you actually want something stronger than both your player table team_id IND and player_id FK. Ie check for player that team_id is paired in club with a club_id that is paired in contract with a person_id that is person_id.

Such complicated constraints are actually unnecessary if your design involved, say, roster(club_id,team_id,player_id) and didn't have team_id in player and probably didn't have contract either.

No, we cannot "see" from a schema that "person references club through contract". (And that phrase is a poor way of expressing what you mean.) We cannot tell that from the tables and keys. Every table holds rows that satisfy some statement parameterized by its column names; you must give these statements. Eg for player: person [person_id] plays on a team identified by [team_id] & some club. You must also give all business rules that restrict what situations can arise. Eg: A person can be contracted to at most one club. A team belongs to at most one club. A user (nor we) cannot use the database without the statements and you (nor we) cannot determine the constraints without the statements and business rules. Write them out.




回答2:


What should be unique is the team_id column in your team table. Currently what you've done means a teamid can be repeated as long as it goes with different clubids, which is incorrect.

The FK to club takes care already of the 0-n relationship you mention.

Edit: going on with the relationship issue.

Since a team may belong to only 1 club,

  1. the relationship should be represented by a foreign key (club_id pointing at the club table)
  2. the primary key should not include the club_id

Why that? Including the club_id in the primary key would mean that if the team changes club, it would fondamentally change what team it is. Apart from the fact that I don´t see why a team would change a club, if that ever happened, the team would be exactly the same object, perfectly independant from the club.

It would be a terrible conception error to do otherwise.



来源:https://stackoverflow.com/questions/23981709/referencing-part-of-the-composite-primary-key

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!