2014-02-06 2 views
1

У меня есть проблема, что я не уверен, как правильно ее решить. Мой текущий проект состоит из двух таблиц. сотрудника и сотрудника_management. Моя СУБД - это MySQL.Конструкция реляционной таблицы: предотвращать циркулярные ссылки

сотрудник:

CREATE TABLE `employee` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

employee_management:

CREATE TABLE `employee_management` (
    `manager_id` int(11) NOT NULL, 
    `employee_id` int(11) NOT NULL, 
    UNIQUE INDEX `association` (`manager_id`,`employee_id`), 
    FOREIGN KEY (`manager_id`) 
    REFERENCES employee(id) 
    ON UPDATE CASCADE ON DELETE RESTRICT, 
    FOREIGN KEY (`employee_id`) 
    REFERENCES employee(id) 
    ON UPDATE CASCADE ON DELETE RESTRICT 
) 

Тестовые данные:

INSERT INTO employee(name) VALUES(
    'Bob Smith' 
) 
INSERT INTO employee(name) VALUES(
    'Bill Smith' 
) 

INSERT INTO employee_management(manager_id, employee_id) VALUES(
    1,2 
) 
INSERT INTO employee_management(manager_id, employee_id) VALUES(
    2,1 
) 

Выбор строки из employee_management показывает это:

+------------+-------------+ 
| manager_id | employee_id | 
+------------+-------------+ 
|   2 |   1 | 
|   1 |   2 | 
+------------+-------------+ 

Возвращенные строки указывают, что Билл Смит управляет Бобом Смитом, а Боб Смит управляет Биллом Смитом, который, я считаю, является круглой ссылкой. Два человека, управляющих друг другом, не имеют смысла. Я думал, что UNIQUE INDEX будет препятствовать вставке строк, которые имели бы существующее сочетание значений, но это не сработало. Я знаю, что могу предотвратить это на уровне приложений, но я не уверен, что это подходящая вещь, но это то, что должно быть применено на уровне приложения, или есть что-то, что я могу сделать для предотвратить циркулярную ссылку? Менеджеры также не должны иметь менеджеров.

+0

Может ли Боб Смит управлять Диком Джонсом, который управляет Биллом Смитом, который управляет Бобом Смитом? – Strawberry

+1

Менеджеры не имеют менеджеров. –

+0

В этом случае рассмотрим возможность переключения на модель вложенного набора - или, как вы говорите, управлять бизнес-логикой на уровне приложения. – Strawberry

ответ

1

Основной причиной такой сложности является низкая поддержка объявления этих ограничений СУБД.

Можно утверждать только два типа таблиц ограничений Декоративно:

  • Уникально, идентифицирующие атрибуты (ключи)
  • требования подмножестве ссылающегося обратно в ту же таблицу, и в этом случае требование подмножества является ограничение таблицы (внешним ключ к тому же таблица).

Внедрение всех других типов ограничений таблиц требует разработки процедурного кода целостности данных . На практике это означает, что вам часто приходится прибегать к инициированной процедурной стратегии или реализации на бизнес-уровне вашего приложения.

После this и this post, вы можете использовать триггер, чтобы проверить, если запрос ниже результатов больше 0, то откатить изменения

SELECT count(*) FROM employee_management e1 
      WHERE EXISTS (SELECT * FROM employee_management e2 
      WHERE 
      e1.manager_id = e2.employee_id 
      AND 
      e1.employee_id = e2.manager_id) 

В примечании вы можете иметь внешний ключ от работника к работнику показывая менеджера работника.

+0

«Плохая поддержка» специфична для MySQL. Во многих других СУБД (например, Postgres) вы можете легко определить индекс, который предотвратил бы «(1,2)» и «(2,1)» –

+0

Я тоже не смог найти решение для индексов в Oracle! –

+1

См. Здесь: http://sqlfiddle.com/#!4/e5ac7/1 для примера –

0

Мне нужен триггер, чтобы сделать именно это на SQL Server - если это помогает кому-то решить ту же проблему, вот определение триггера.

CREATE TRIGGER iu_erp_user 
ON [dbo].[erp_user] 
FOR INSERT, UPDATE 
AS 
BEGIN 
SET NOCOUNT ON 

declare @erp_user_id int 
declare @supervisor_supervisor_id int 

select @erp_user_id = i.erp_user_id, @supervisor_supervisor_id = eu.supervisor_id 
from inserted i 
left join erp_user eu on i.supervisor_id = eu.erp_user_id 

if @erp_user_id = @supervisor_supervisor_id 
begin 
    RAISERROR('Circular reference created.', 16, 1) 
    ROLLBACK TRAN 
end 

END 
GO 
Смежные вопросы