Нет общего правила или передовой практики, внешние ключи не должны быть обнуляемыми. Много раз это имеет смысл для субъекта не иметь отношения с другим сущностью. Например, у вас может быть таблица художников, которую вы отслеживаете, но на данный момент у вас нет компакт-дисков, записанных этими художниками.
Что касается носителя (CD, DVD, BluRay), который может быть как музыкальным, так и аудио или программным обеспечением, вы можете иметь таблицу с общей информацией, а затем два внешних ключа, по одному на каждую таблицу расширений (AudioData и SoftwareData), но должен быть NULL
. Это представляет собой ситуацию, которая, среди прочего, называется исключительной дугой. Этот обычно считается ... проблематичным.
Подумайте о суперклассе и двух производных классах на языке OO, таком как Java или C++. Один из способов представить, что в реляционной схеме является:
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)
);
Теперь, если вы ищете фильм, вы будете искать таблицу AVData, а затем присоединиться к таблице медиа для остальной части информации и так далее с программным обеспечением или игр. Если у вас есть значение ID, но не знаете, что это такое, выполните поиск в таблице Media, а значение Type сообщит вам, из какой из трех (или более) таблиц данных для соединения. Дело в том, что FK ссылается на на общую таблицу, а не на нее.
Конечно, фильм или игра или программное обеспечение могут быть выпущены на нескольких типах носителей, поэтому вы можете иметь таблицы пересечений между таблицей Media
и соответствующими таблицами данных. Otoh, те, как правило, обозначаются разными SKU, поэтому вы можете также рассматривать их как разные элементы.
Код, как и следовало ожидать, может быть довольно сложным, хотя и не слишком плохим.Otoh, наша цель дизайна - это не простота кода, а целостность данных. Это делает невозможным смешивание, например, игровых данных с элементом фильма. И вы избавляетесь от наличия набора полей, где только одно должно иметь значение, а остальные должны быть нулевыми.
MySQL не применяет ограничения CHECK. –
Затем используйте триггеры. – TommCatt
Огромное спасибо, я должен был положить в тупик решение из-за крайних сроков, но это поможет в будущем. –