Không có quy tắc chung hoặc Phương pháp hay nhất nào mà các khóa ngoại không được để trống. Nhiều khi việc một thực thể không có mối quan hệ với một thực thể khác là hoàn toàn hợp lý. Ví dụ:bạn có thể có một bảng các nghệ sĩ mà bạn theo dõi nhưng hiện tại, bạn không có đĩa CD nào được ghi bởi các nghệ sĩ đó.
Đối với việc có Phương tiện (CD, DVD, BluRay) có thể là nhạc / âm thanh hoặc phần mềm, bạn có thể có một bảng với thông tin chung và sau đó là hai khóa ngoại, một cho mỗi bảng mở rộng (AudioData và SoftwareData), nhưng một phải là NULL
. Điều này đưa ra một tình huống được gọi là, trong số những thứ khác, một vòng cung độc quyền. Cái này thường được coi là ... có vấn đề.
Hãy nghĩ về một lớp cha và hai lớp dẫn xuất trong một ngôn ngữ OO như Java hoặc C ++. Một cách để biểu diễn điều đó trong lược đồ quan hệ là:
create table Media(
ID int not null, -- identity, auto_generated, generated always as identity...
Type char( 1 ) not null,
Format char( 1 ) not null,
... <other common data>,
constraint PK_Media primary key( ID ),
constraint FK_Media_Type foreign key( Type )
references MediaTypes( ID ), -- A-A/V, S-Software, G-Game
constraint FK_Media_Format foreign key( Format )
references MediaFormats( ID ) -- C-CD, D-DVD, B-BluRay, etc.
);
create unique index UQ_Media_ID_Type( ID, Type ) on Media;
create table AVData( -- For music and video
ID int not null,
Type char( 1 ) not null,
... <audio-only data>,
constraint PK_AVData primary key( ID ),
constraint CK_AVData_Type check( Type = 'A',
constraint FK_AVData_Media foreign key( ID, Type )
references Media( ID, Type )
);
create table SWData( -- For software, data
ID int not null,
Type char( 1 ) not null,
... <software-only data>,
constraint PK_SWData primary key( ID ),
constraint CK_SWData_Type check( Type = 'S',
constraint FK_SWData_Media foreign key( ID, Type )
references Media( ID, Type )
);
create table GameData( -- For games
ID int not null,
Type char( 1 ) not null,
... <game-only data>,
constraint PK_GameData primary key( ID ),
constraint CK_GameData_Type check( Type = 'G',
constraint FK_GameData_Media foreign key( ID, Type )
references Media( ID, Type )
);
Bây giờ nếu bạn đang tìm kiếm một bộ phim, bạn tìm kiếm bảng AVData, sau đó kết hợp với bảng Media để biết thông tin còn lại, v.v. với phần mềm hoặc trò chơi. Nếu bạn có giá trị ID nhưng không biết nó là loại gì, hãy tìm kiếm trong bảng Phương tiện và giá trị Loại sẽ cho bạn biết nên kết hợp với bảng dữ liệu nào trong ba (hoặc nhiều hơn) bảng dữ liệu. Vấn đề là FK đang đề cập đến đến bảng chung, không phải từ nó.
Tất nhiên, một bộ phim hoặc trò chơi hoặc phần mềm có thể được phát hành trên nhiều loại phương tiện, vì vậy bạn có thể có các bảng giao nhau giữa Media
bảng và các bảng dữ liệu tương ứng. Otoh, chúng thường được gắn nhãn với các SKU khác nhau, vì vậy bạn có thể muốn coi chúng như các mặt hàng khác nhau.
Mã, như bạn có thể mong đợi, có thể khá phức tạp, mặc dù không quá tệ. Otoh, mục tiêu thiết kế của chúng tôi không phải là sự đơn giản của mã mà là tính toàn vẹn của dữ liệu. Điều này làm cho nó không thể trộn, chẳng hạn, dữ liệu trò chơi với một mục phim. Và bạn loại bỏ việc có một tập hợp các trường trong đó chỉ một trường phải có giá trị và các trường khác phải rỗng.