PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Как да имам външен ключ, сочещ към два първични ключа?

Правила за 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;

Основен съвет:



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. postgresql, не можа да идентифицира колона в типа данни на запис

  2. Достатъчно ефективни ли са postgres JSON индексите в сравнение с класическите нормализирани таблици?

  3. Агрегирани функции на множество обединени таблици

  4. Уебинар:Нови функции в PostgreSQL 11 [Продължаване]

  5. Как да промените ID на таблица от сериен към идентичност?