Det finns ingen generell regel eller bästa praxis att främmande nycklar inte ska vara nullbara. Många gånger är det helt vettigt för en enhet att inte ha en relation med en annan enhet. Du kan till exempel ha en tabell över artister som du spårar, men för tillfället har du inga CD-skivor inspelade av dessa artister.
När det gäller att ha media (CD, DVD, BluRay) som kan vara antingen musik/ljud eller programvara, kan du ha en tabell med informationen gemensam och sedan två främmande nycklar, en till varje förlängningstabell (AudioData och SoftwareData), men en måste vara NULL
. Detta presenterar en situation som bland annat kallas en exklusiv båge. Detta anses allmänt vara...problematisk.
Tänk på en superklass och två härledda klasser i ett OO-språk som Java eller C++. Ett sätt att representera det i ett relationsschema är:
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 )
);
Om du nu letar efter en film, söker du i AVData-tabellen, går sedan med i Media-tabellen för resten av informationen och så vidare med programvara eller spel. Om du har ett ID-värde men inte vet vilken typ det är, sök i Media-tabellen och Type-värdet kommer att berätta vilken av de tre (eller fler) datatabellerna du ska gå med i. Poängen är att FK syftar till den generiska tabellen, inte från den.
Naturligtvis kan en film eller ett spel eller programvara släppas på mer än en mediatyp, så du kan ha skärningstabeller mellan Media
tabell och respektive datatabell. Otoh, de är vanligtvis märkta med olika SKU:er så du kanske också vill behandla dem som olika föremål.
Koden kan, som du kanske förväntar dig, bli ganska komplicerad, men inte så illa. Otoh, vårt designmål är inte enkel kod utan dataintegritet. Detta gör det omöjligt att blanda till exempel speldata med ett filmobjekt. Och du slipper ha en uppsättning fält där bara ett måste ha ett värde och de andra måste vara null.