Правила за FK ограничения
За да отговорите на въпроса в заглавието и в края на вашия текст:
„Все пак бих искал да знам как да имам един външен ключ, препращащ към два първични ключа.“
Това е невъзможно.
-
ВЪНШЕН КЛЮЧ
ограничението може да сочи само към един таблица и всяка таблица може да има само еднаОСНОВЕН КЛЮЧкод>
ограничение. -
Или можете да имате няколко
ВЪНШЕН КЛЮЧ
ограничения за една и съща колона(и), препращаща към единПЪРВИЧЕН КЛЮЧ
на (различна) таблица всеки. (Рядко полезно.)
Въпреки това , единичен PK или FK can обхваща множество колони.
И FK може да препраща към всяка изрично дефинирана уникална (набор от) колона(и) в целта, а не само към PK. Ръководството:
PK с няколко колони или УНИКАЛЕН
ограничението може да се посочи само от FK ограничение с много колони със съответстващи типове колони.
Какво питате
Тъй като не е разрешено да се използва една и съща колона повече от веднъж в списъка с колони на UNIQUE
или PRIMARY KEY
ограничение, целевият списък на FOREIGN KEY
също така не може да използва една и съща колона повече от веднъж. Но нищо не ни пречи да използваме една и съща колона повече от веднъж в източника списък. Тук се крие потенциалът да приложите това, което питате (но вероятно не сте искали):
„В team_statistics
таблица team_statistics.team_id
трябва да бъде външен ключ, който препраща към matches.team_id
и matches.team_id1
"
Комбинацията от (team_id, team_id1)
в таблицата съответства
трябва да се дефинира UNIQUE
. Стойности в team_statistics.team_id
ще бъдат ограничени до случаи с team =team1
в таблицата съответства
като логично следствие:
ALTER TABLE matches
ADD constraint matches_teams_groups_uni UNIQUE (team_id, team_id1);
ALTER TABLE team_statistics
ADD constraint team_statistics_team_group fkey
FOREIGN KEY (team_id, team_id) -- same column twice!
REFERENCES matches(team_id, team_id1);
Може дори да има смисъл за определени настройки, но не и за вашите.
Това, от което вероятно се нуждаете
Моето обосновано предположение е, че искате нещо подобно:
(match_id, team_id)
в таблица team_statistics
трябва да бъде външен ключ, който препраща към или (match_id, team_id)
или (match_id, team_id1)
в таблицата съответства
.
И това не е възможно с FK ограничения и само две таблици. Вие можете злоупотреба с ЧЕК
ограничение с фалшив IMMUTABLE
функция и я направете НЕВАЛИДНА
. Вижте глава „По-евтино с ограничение CHECK“ в този отговор:
Но това е напреднал трик и по-малко надежден. Не е моето предложение тук, така че няма да го разяснявам. Предлагам да нормализиране вашата схема по полезен начин, като:
CREATE TABLE team (team_id serial PRIMARY KEY
, team text NOT NULL UNIQUE); -- add more attributes for team
CREATE TABLE match (match_id serial PRIMARY KEY); -- add more attributes for match
CREATE TABLE match_team (
match_id int REFERENCES match -- short notation for FK
, team_id int REFERENCES team
, home boolean -- TRUE for home team, FALSE for away team
, innings_score int
-- more attributes of your original "team_statistics"
, PRIMARY KEY (match_id, team_id, home) -- !!! (1st column = match_id)
, UNIQUE (team_id, match_id) -- optional, (1st column = team_id)
);
начало
маркира отбора домакин на мача, но чрез включване в PK също ограничава до максимум два отбора на мач . (PK колоните са дефинирани NOT NULL
имплицитно.)
Незадължителният UNIQUE
ограничение върху (team_id, match_id)
не позволява на отборите да играят срещу себе си. Чрез използването на обърната последователност от индексни колони (без значение за прилагането на правилото) това също осигурява индекс, допълващ PK, което обикновено също е полезно. Вижте:
Вие можете добавете отделна match_team_statistics
, но това би било само незадължително разширение 1:1 към match_team
сега. Като алтернатива просто добавете колони към match_team
.
Може да добавя изгледи за типични дисплеи, като:
CREATE VIEW match_result AS
SELECT m.match_id
, concat_ws(' : ', t1.team, t2.team) AS home_vs_away_team
, concat_ws(' : ', mt1.innings_score, mt2.innings_score) AS result
FROM match m
LEFT JOIN match_team mt1 ON mt1.match_id = m.match_id AND mt1.home
LEFT JOIN team t1 ON t1.team_id = mt1.team_id
LEFT JOIN match_team mt2 ON mt2.match_id = m.match_id AND NOT mt2.home
LEFT JOIN team t2 ON t2.team_id = mt2.team_id;
Основен съвет: