Quy tắc cho các ràng buộc FK
Để trả lời câu hỏi trong tiêu đề và ở cuối văn bản của bạn:
"Tôi vẫn muốn biết cách có một khóa ngoại tham chiếu đến hai khóa chính."
Điều đó là không thể.
-
NGOẠI KHÓA
ràng buộc chỉ có thể trỏ đến một bảng và mỗi bảng chỉ có thể có mộtKEY CHÍNH
ràng buộc. -
Hoặc bạn có thể có nhiều
NGOẠI KHÓA
các ràng buộc trên cùng (các) cột tham chiếu đến mộtCHÌA KHÓA CHÍNH
của mỗi bảng (khác nhau). (Hiếm khi hữu ích.)
Tuy nhiên , một PK hoặc FK có thể mở rộng nhiều cột.
Và FK có thể tham chiếu bất kỳ (các) cột (tập hợp) cột duy nhất được xác định rõ ràng nào trong mục tiêu, không chỉ PK. Hướng dẫn sử dụng:
PK nhiều cột hoặc DUY NHẤT
ràng buộc chỉ có thể được tham chiếu bởi ràng buộc FK nhiều cột với các loại cột phù hợp.
Những gì bạn hỏi
Vì không được phép sử dụng cùng một cột nhiều lần trong danh sách cột của một UNIQUE
hoặc PRIMARY KEY
ràng buộc, danh sách đích của một FOREIGN KEY
cũng không thể sử dụng cùng một cột nhiều hơn một lần. Nhưng không có gì ngăn chúng tôi sử dụng cùng một cột nhiều lần trong nguồn danh sách. Đây là tiềm năng để triển khai những gì bạn đang yêu cầu (nhưng có thể không cố ý):
"Trong team_stosystem
lập bảng team_stosystem.team_id
phải là khóa ngoại tham chiếu đến match.team_id
và trận đấu.team_id1
"
Sự kết hợp của (team_id, team_id1)
trong bảng phù hợp
sẽ cần được xác định UNIQUE
. Các giá trị trong team_stosystem.team_id
sẽ bị hạn chế trong các trường hợp có team =team1
trong bảng phù hợp
như một hệ quả logic:
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);
Thậm chí có thể có ý nghĩa đối với một số thiết lập nhất định, nhưng không phải của bạn.
Những gì bạn có thể cần
Tôi đoán là bạn muốn một cái gì đó như thế này:
(match_id, team_id)
trong bảng team_stosystem
phải là khóa ngoại tham chiếu đến một trong hai (match_id, team_id)
hoặc (match_id, team_id1)
trong bảng phù hợp
.
Và điều đó là không thể với các ràng buộc FK và chỉ hai bảng. Bạn có thể lạm dụng KIỂM TRA
ràng buộc với IMMUTABLE
giả hoạt động và làm cho nó KHÔNG HỢP LỆ
. Xem chương "Rẻ hơn với ràng buộc KIỂM TRA" trong câu trả lời này:
Nhưng đó là thủ thuật tiên tiến và ít đáng tin cậy hơn. Không phải đề xuất của tôi ở đây, vì vậy tôi sẽ không nói chi tiết. Tôi khuyên bạn nên chuẩn hóa lược đồ của bạn theo cách hữu ích, như:
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
đánh dấu đội chủ nhà của trận đấu nhưng bằng cách đưa vào PK, cũng hạn chế ở mức tối đa hai đội mỗi trận . (Các cột PK được xác định NOT NULL
ngầm định.)
UNIQUE
tùy chọn ràng buộc về (team_id, match_id)
ngăn các đội chơi với chính họ. Bằng cách sử dụng trình tự đảo ngược của các cột chỉ mục (không liên quan đến việc thực thi quy tắc), điều này cũng cung cấp một chỉ mục bổ sung cho PK, thường cũng hữu ích. Xem:
Bạn có thể thêm một match_team_stosystem
riêng biệt , nhưng đó sẽ chỉ là phần mở rộng 1:1 tùy chọn cho match_team
bây giờ. Ngoài ra, chỉ cần thêm các cột vào match_team
.
Tôi có thể thêm lượt xem cho các màn hình điển hình, như:
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;
Lời khuyên cơ bản: