Nie ma ogólnej zasady ani najlepszych praktyk, w których klucze obce nie powinny mieć wartości null. Wiele razy ma sens, aby jednostka nie miała związku z inną istotą. Na przykład możesz mieć tabelę wykonawców, których śledzisz, ale w tej chwili nie masz płyt CD nagranych przez tych artystów.
Jeśli chodzi o nośniki (CD, DVD, BluRay), które mogą być muzyką/audio lub oprogramowaniem, możesz mieć tabelę ze wspólnymi informacjami, a następnie dwa klucze obce, po jednym dla każdej tabeli rozszerzeń (AudioData i SoftwareData), ale jeden musi być NULL
. Przedstawia to sytuację zwaną między innymi łukiem ekskluzywnym. To jest ogólnie uważany za… problematyczny.
Pomyśl o superklasie i dwóch klasach pochodnych w języku OO, takim jak Java lub C++. Jednym ze sposobów przedstawienia tego w schemacie relacyjnym jest:
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 )
);
Teraz, jeśli szukasz filmu, przeszukujesz tabelę AVData, a następnie dołączasz do tabeli Media, aby uzyskać resztę informacji i tak dalej z oprogramowaniem lub grami. Jeśli masz wartość identyfikatora, ale nie wiesz, jaki to rodzaj, przeszukaj tabelę Media, a wartość Type wskaże, z którą z trzech (lub więcej) tabel danych należy się połączyć. Chodzi o to, że FK odnosi się do do tabela ogólna, a nie z niej.
Oczywiście film, gra lub oprogramowanie mogą być wydane na więcej niż jednym typie nośnika, więc możesz mieć tabele przecięcia między Media
tabela i odpowiednie tabele danych. Otoh, są one zazwyczaj oznaczone różnymi kodami SKU, więc możesz traktować je również jako różne przedmioty.
Kod, jak można się spodziewać, może stać się dość skomplikowany, choć nie taki zły. Otoh, naszym celem projektowym nie jest prostota kodu, ale integralność danych. Uniemożliwia to mieszanie np. danych z gry z elementem filmowym. I pozbywasz się zestawu pól, z których tylko jedno musi mieć wartość, a pozostałe muszą mieć wartość null.