Я определяю объект, который представляет страницу, состоящую из одного или нескольких элементов Part. На каждой странице должна быть определена хотя бы одна часть. Каждая Часть принадлежит только одной Странице. Один из элементов части будет «главной» частью на странице, а все остальные будут полагаться на главную часть. Помимо этой разницы, главная и ведомая части ведут себя одинаково.
Я рассматриваю несколько разных способов выразить это в SQL. Я могу придумать пару вариантов, каждый со своими преимуществами и недостатками. Я уверен, что нет единого «правильного ответа», но я ищу отзывы о том, какие подходы имеют скрытые сложности, которые позже укусят меня за задницу. Ограничения и триггеры CASCADE желательны, чтобы другие стороны могли безопасно изменять таблицы, когда это необходимо, но я хотел бы избежать конструкции, требующей запутанной и сложной логики.
Во время настройки пользователю нужна возможность выбрать мастер, не позволяя помечать 0 или несколько частей как первичные. Во время выполнения каждая часть должна определить, является ли она мастером или должна полагаться на мастер для получения информации.
Моя целевая платформа — Microsoft SQL Server 2008 или выше. В приведенных ниже примерах используются ключи int просто потому, что это делает примеры меньше — я бы предпочел использовать ключи GUID при реализации.
Вариант 1 – пометить основную часть
create table [Page] (
PageKey int identity primary key,
PageName varchar(30) not NULL
)
go
create table [Part] (
PartKey int identity primary key,
PartName varchar(30) not NULL,
PageKey int not null,
constraint fkPage foreign key (PageKey) references [Page](PageKey),
IsMasterPart bit not NULL default 0
)
Преимущества: простота понимания данных; аналогичный подход используется в базовом приложении, которое я улучшаю; запись детали знает, что она является мастером, без поиска данных в записи страницы. Недостатки: принудительное использование одного и только одного мастера потребует, чтобы триггеры отклоняли обновления, нарушающие правило, или код прикладного уровня, или и то, и другое.
Вариант 2 — внешний ключ на странице
create table [Page] (
PageKey int identity primary key,
PageName varchar(30) not NULL,
MasterPartKey int not NULL
)
go
create table [Part] (
PartKey int identity primary key,
PartName varchar(30) not NULL,
PageKey int not null,
constraint fkPage foreign key (PageKey) references [Page](PageKey),
IsMasterPart bit not NULL default 0
)
go
alter table [Page]
add constraint fkMasterPart
foreign key (MasterPartKey) references [Part](PartKey)
Преимущества: DRI защищает от удаления мастер-детали; может иметь только одну основную деталь; не может иметь Страницу без мастер-части. Недостатки: потенциальная проблема курицы и яйца при назначении значений PK/FK.
- Каков шаблон (или есть ли он) для вставки родителя и первого обязательного дочернего элемента в одной операции?
- Каков шаблон (или есть ли он) для удаления последнего дочернего и родительского элементов, который по-прежнему предотвратит удаление страницы и основной части, когда существуют другие части?
Я подозреваю, что упустил одну или две детали, необходимые для хорошего ответа. Пожалуйста, спросите, и я обновлю.