Aturan untuk batasan FK
Untuk menjawab pertanyaan di judul dan di akhir teks Anda:
"Saya masih ingin tahu cara memiliki satu kunci asing yang mereferensikan dua kunci utama."
Itu tidak mungkin.
-
FOREIGN KEY
batasan hanya dapat menunjuk ke satu tabel dan setiap tabel hanya boleh memiliki satuPRIMARY KEY
kendala. -
Atau Anda dapat memiliki beberapa
FOREIGN KEY
batasan pada kolom yang sama yang mereferensikan satuPRIMARY KEY
dari tabel (berbeda) masing-masing. (Jarang berguna.)
Namun , satu PK atau FK bisa span beberapa kolom.
Dan FK dapat mereferensikan setiap kolom (kumpulan) unik yang didefinisikan secara eksplisit dalam target, bukan hanya PK. Manual:
PK multikolom atau UNIQUE
batasan hanya dapat direferensikan oleh batasan FK multikolom dengan jenis kolom yang cocok.
Apa yang Anda tanyakan
Karena tidak diperbolehkan menggunakan kolom yang sama lebih dari satu kali dalam daftar kolom UNIQUE
atau PRIMARY KEY
kendala, daftar target FOREIGN KEY
juga tidak dapat menggunakan kolom yang sama lebih dari satu kali. Namun tidak ada yang menghalangi kami untuk menggunakan kolom yang sama lebih dari satu kali di sumber daftar. Di sinilah letak potensi untuk menerapkan apa yang Anda minta (tetapi mungkin tidak bermaksud demikian):
"Dalam team_statistics
tabel team_statistics.team_id
harus berupa kunci asing yang mereferensikan matches.team_id
dan matches.team_id1
"
Kombinasi (team_id, team_id1)
dalam tabel matches
perlu didefinisikan UNIQUE
. Nilai dalam team_statistics.team_id
akan dibatasi untuk kasus dengan team = team1
dalam tabel matches
sebagai konsekuensi logis:
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);
Bahkan mungkin masuk akal untuk penyiapan tertentu, tetapi tidak untuk Anda.
Yang mungkin Anda butuhkan
Dugaan saya yang terpelajar adalah Anda menginginkan sesuatu seperti ini:
(match_id, team_id)
dalam tabel team_statistics
harus berupa kunci asing yang mereferensikan salah satu (match_id, team_id)
atau (match_id, team_id1)
dalam tabel matches
.
Dan itu tidak mungkin dengan batasan FK dan hanya dua tabel. Anda bisa menyalahgunakan CHECK
kendala dengan IMMUTABLE
fake palsu fungsi dan jadikan NOT VALID
. Lihat bab "Lebih murah dengan batasan PERIKSA" dalam jawaban ini:
Tapi itu tipuan tingkat lanjut dan kurang bisa diandalkan. Bukan saran saya di sini, jadi saya tidak akan menguraikannya. Saya menyarankan untuk menormalkan skema Anda dengan cara yang bermanfaat, seperti:
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)
);
home
menandai tim tuan rumah pertandingan tetapi, dengan dimasukkan dalam PK, juga membatasi hingga maksimal dua tim per pertandingan . (Kolom PK didefinisikan NOT NULL
secara implisit.)
Opsi UNIQUE
batasan pada (team_id, match_id)
mencegah tim bermain melawan diri mereka sendiri. Dengan menggunakan urutan kolom indeks terbalik (tidak relevan untuk menegakkan aturan) ini juga menyediakan indeks yang melengkapi PK, yang biasanya juga berguna. Lihat:
Anda bisa tambahkan match_team_statistics
yang terpisah , tapi itu hanya akan menjadi ekstensi 1:1 opsional untuk match_team
sekarang. Atau cukup tambahkan kolom ke match_team
.
Saya mungkin menambahkan tampilan untuk tampilan biasa, seperti:
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;
Saran dasar: