Этот вопрос в определенном смысле очень специфический, и некоторые люди могут утверждать, что он слишком локализован. Однако есть еще одна общеприменимая идея, которая может пригодиться другим людям в будущем, поэтому не всегда верно, что вопрос слишком конкретен.
Действительно интересная часть этих бизнес-правил: (мой акцент добавлен)
Один отдел может быть назначен более чем в один регион, только если каждый
область принадлежит другой зоне .
Вот схема, которая почти декларирует все заявленные бизнес-правила декларативно, не прибегая к каким-либо триггерам.
create table ZONE
( ID int not null
, NAME varchar(50) not null
, constraint PK_ZONE primary key clustered (ID)
)
create table REGION
( ZONE_ID int not null
, REGION_ID int not null
, NAME varchar(50) not null
, constraint PK_REGION primary key clustered (ZONE_ID, REGION_ID)
, conttraint FK_REGION__ZONE foreign key (ZONE_ID)
references ZONE (ID)
)
create table DEPARTMENT
( ID int not null
, NAME varchar(50) not null
, constraint PK_DEPARTMENT primary key clustered (ID)
)
create table EMPLOYEE
( ID int not null
, NAME varchar(50) not null
, DEPT_ID int not null
, constraint PK_EMPLOYEE primary key clustered (ID)
, constraint FK_EMPLOYEE__DEPARTMENT foreign key (DEPT_ID)
references DEPARTMENT (ID)
)
Приведенные выше таблицы довольно очевидны. Однако есть одна особенность: у таблицы REGION
есть составной первичный ключ, который включает FK в ZONE
. Это полезно для распространения ограничения на то, что отделы должны различаться в пределах зоны.
Для присвоения отделов регионам требуется таблица пересечений:
create table DEPT_ASGT -- Department Assignment
( REGION_ID int not null
, DEPT_ID int not null
, ZONE_ID int not null
, constraint PK_DEPT_ASGT (REGION_ID, DEPT_ID)
, constraint FK_DEPT_ASGT__REGION foreign key (ZONE_ID, REGION_ID)
references REGION (ZONE_ID, ID)
, constraint FK_DEPT_ASGT__DEPARTMENT foreign key (DEPT_ID)
references DEPARTMENT (ID)
, constraint UN_DEPT_ASGT__ZONES unique nonclustered (ZONE_ID, DEPT_ID)
)
Эта таблица пересечений является довольно обычной, поскольку она имеет внешний ключ для каждой из таблиц, которые она связывает. Что особенного в этой таблице пересечений, так это уникальное ограничение. Это то, что обеспечивает соблюдение правила, согласно которому отдел не может находиться в двух разных регионах в одной зоне.
Наконец, нам нужно сопоставить сотрудников с отделами и регионами. Для этого требуется другая таблица пересечений:
create table EMP_ASGT -- Employee Assignment
( REGION_ID int not null
, DEPT_ID int not null
, EMPLOYEE_ID int not null
, constraint PK_EMP_ASGT (REGION_ID, DEPT_ID, EMPLOYEE_ID)
, constraint FK_EMP_ASGT__DEPT_ASGT (REGION_ID, DEPT_ID)
references DEPT_ASGT (REGION_ID, DEPT_ID)
, constraint FK_EMP_ASGT__EMPLOYEE (EMPLOYEE_ID) refernces EMPLOYEE (ID)
)
Вы заметите, что таблица EMPLOYEE
имеет внешний ключ для DEPARTMENT
- это обеспечивает соблюдение правила, согласно которому каждый сотрудник может принадлежать только одному отделу. В таблицу EMP_ASGT
добавлены сведения о том, в каких регионах участвует сотрудник. Так как сотрудник может быть не вовлечен в каждый регион, к которому относится его или ее отдел, таблица EMP_ASGT
связывает сотрудников только с теми регионами, где у них есть некоторые участие.
Это единственное место, где необходим триггер или какая-то другая процедурная логика. Вы должны убедиться, что EMPLOYEE.department_id остается согласованным с записями в EMP_ASGT. Вы можете попытаться внедрить это в декларативную ссылочную целостность, сделав PK EMPLOYEE соединением ID и DEPT_ID, но это заставит вас решить, хотите ли вы нарушить 3NF или сделать так, чтобы ваш отдел персонала изменил процедурно-уродливый беспорядок. В конце концов, небольшой триггер, чтобы убедиться, что EMP_ASGT не расходится с EMPLOYEE.DEPT_ID, будет намного меньше проблем.