Дизайн базы данных для рекурсивных отношений

Рассмотрим этот случай, когда я пытаюсь смоделировать базу данных для компании:

  • Сущности: Employees, Managers, Departments.
  • Employee работает только в 1 Department, в то время как Department может иметь много Employees работающих в нем.
  • Manager может управлять только 1 Department и аналогично Department может иметь только 1 Manager.
  • Manager руководит многими Employees, но Employee контролируется только одним Manager.

Теперь у меня есть 2 способа смоделировать это:

Первое решение:

Я буду считать, что объект Manager наследуется от объекта Employee, учитывая, что я буду хранить данные, которые уникальны для менеджеров (например, бонус и статус).

Первое решение

  • Поскольку отношение между Department и Employee равно 1:N, я помещу Department Id в качестве внешнего ключа в таблицу Employee для отношения Works.

  • Поскольку отношение между Department и Manager равно 1:1, я помещу Department Id в качестве внешнего ключа в таблицу Manager для отношения Manages.

Проблема: как представить рекурсивную связь между Manager и Employee?


Второе решение:

Я буду считать, что объект Manager не нужен, так как другие Employees могут также иметь Bonus и Status. (На самом деле я добавил эти 2 атрибута только для того, чтобы посмотреть, как это смоделировать в обоих случаях) Второе решение

  • Поскольку отношение между Department и Employee равно 1:N, я помещу Department Id в качестве внешнего ключа в таблицу Employee для отношения Works.
  • Поскольку отношение между Employee и Manager равно 1:N, я помещу Employee Id в качестве внешнего ключа в таблицу Employee для отношения Supervises и назову его Manager Id.

Проблема: как представить связь между Manager и Department?


Вопросы:

  1. Есть ли очевидные ошибки в обоих проектах?
  2. Как решить каждую проблему в обоих случаях?
  3. Есть ли лучшее решение, чем эти два?

person Songo    schedule 01.04.2012    source источник


Ответы (5)


Я бы, наверное, пошел с чем-то вроде:

введите здесь описание изображения

Данная модель имеет следующие характеристики:

  • Manager "inherits" employee.
    • To represent an employee, insert a single row in EMPLOYEE.
    • Чтобы представить менеджера, вставьте одну строку в EMPLOYEE и одну строку в MANAGER.
  • В отделе может быть несколько сотрудников.
  • В каждом отделе есть ровно 1 менеджер, и каждый менеджер управляет 0 или 1 отделом.
  • Супервайзером может быть рядовой сотрудник или менеджер.
  • Departments are not required to "match":
    • A supervisor can work in different department from the supervised employee.
    • Менеджер может управлять другим отделом, в котором он работает.
    • Если супервайзер является менеджером, то отдел(ы), которым он управляет, отдел(ы), в котором он работает, и отдел(ы) его/ее подчиненных могут быть разными.

ПРИМЕЧАНИЕ. Если ваша СУБД не поддерживает отложенные ограничения, вам нужно сделать DEPARTMENT.MANAGER_ID нулевым, чтобы разорвать цикл, который в противном случае помешал бы вам вставить новые данные.


Если отделы должны совпадать, вы должны либо использовать технику, специфичную для СУБД (например, триггеры или «специальные» ограничения), либо «распространить» DEPARTMENT_ID в PK сотрудников. Именно это распространение в конечном итоге обеспечивает сопоставление:

введите здесь описание изображения

Поскольку EMPLOYEE_ID должен быть глобально уникальным, он не может оставаться в составном ключе вместе с DEPARTMENT_ID. Итак, мы делаем его альтернативным ключом и вместо этого используем суррогат EMPLOYEE_NO в PK.

Эта модель не позволяет вам иметь менеджера, который управляет одним отделом и работает в другом, или супервайзера, который наблюдает за сотрудниками из другого отдела.


Если вы не знакомы с этим символом...

введите здесь описание изображения

... это обозначает "категорию". В этом контексте вы можете просто интерпретировать это как отношение «1 к 0 или 1» между СОТРУДНИКОМ и МЕНЕДЖЕРОМ.

person Branko Dimitrijevic    schedule 02.04.2012
comment
+1 за наглядный ответ :) Но уверены ли вы, что эта часть верна, Поскольку EMPLOYEE_NO должен быть глобально уникальным, он не может оставаться в составном ключе вместе с DEPARTMENT_ID? Вы имели в виду EMPLOYEE_ID возможно ? и что именно вы подразумеваете под если отделы должны совпадать? - person Songo; 02.04.2012
comment
@Songo Я сделал опечатку (это должен быть ID, а не NO), исправил, спасибо! - person Branko Dimitrijevic; 02.04.2012
comment
@Songo Что касается соответствующих отделов, пожалуйста, перечитайте ответ. По сути, речь идет о том, чтобы (запретить) менеджеру управлять отделом (ами), в котором он не работает, а супервайзеру контролировать сотрудников из другого отдела, отличного от его собственного. - person Branko Dimitrijevic; 02.04.2012
comment
Отлично, я просто убедился, что понял правильно. Другой вопрос, касающийся второй части вашего ответа: почему вы сделали DEPARTMENT_ID в таблице DEPARTMENT как PK, так и FK? Если это FK, на что он ссылается? - person Songo; 02.04.2012
comment
@Songo Есть ВНЕШНИЙ КЛЮЧ: DEPARTMENT {DEPARTMENT_ID, MANAGER_NO}, ссылающийся на MANAGER {DEPARTMENT_ID, MANAGER_NO}. - person Branko Dimitrijevic; 02.04.2012
comment
@Songo Кстати, приношу свои извинения за еще одну опечатку: на второй диаграмме должно быть MANAGER.DEPARTMENT_ID (а не DEPARTMENT_NO). - person Branko Dimitrijevic; 02.04.2012
comment
Хорошо, я здесь официально потерян: D Если моя база данных была совершенно пустой, как я должен заполнить ее данными, чтобы следовать вашей схеме? Если бы это был я, я бы сначала создал пустую Department, но поскольку DEPARTMENT_ID ссылается на таблицу MANAGER, мне придется сначала добавить Manager, но поскольку Manager Id ссылается на таблицу EMPLOYEE, я должен сначала создать Employee, но для создайте Employee. Я должен присвоить значение его DEPARTMENT_ID, которое ссылается на таблицу DEPARTMENT... и я вернулся к исходной точке, чувствуя головокружение: S - person Songo; 02.04.2012
comment
@Songo Вы абсолютно правы - здесь есть циклическая ссылка (на обеих диаграммах). Чтобы разрешить начальную вставку, либо используйте отложенные ограничения, либо (для первой диаграммы) разрешите NULL в DEPARTMENT.MANAGER_ID (как уже упоминалось в моем ответе). Разрешение NULL, вероятно, не вариант на второй диаграмме, поскольку часть дочерней конечной точки FK находится в PK. Если вам действительно нужна вторая диаграмма, и вы используете СУБД без поддержки отложенных ограничений, вам, вероятно, придется использовать специальный механизм базы данных, такой как триггеры, для обеспечения целостности. - person Branko Dimitrijevic; 02.04.2012
comment
На самом деле я впервые слышу об Deferred Constraints, главным образом потому, что я не разработчик базы данных, и большая часть моей работы связана с MySql, который, как я только что узнал, не поддерживает отложенные ограничения: (В любом случае, спасибо за отличное объяснение. Хотел бы я снова проголосовать за ваш ответ: D - person Songo; 02.04.2012

Не вдаваясь в подробности, уверяю вас, что решение «Сотрудник/Менеджер/Отдел» в долгосрочной перспективе является источником неудовольствия (сначала), а затем настоящим PITA (позже) для лиц, ответственных за ведение базы данных и /или разработка его интерфейса. Поэтому я советую вам придерживаться вашего второго предложения.

Что касается отношения менеджер/отдел, у вас есть в основном два способа представления этого отношения. Оба решения разрешают вам сохранить ваше рекурсивное отношение «Менеджер управляет сотрудником» в дополнение к отношению «менеджер управляет отделом», которое вы можете реализовать следующим образом:

1 - первый/простой способ: добавьте идентификатор менеджера/сотрудника в таблицу вашего отдела. Это поле, конечно же, является внешним ключом к таблице сотрудников.

2 - второе/более сложное решение: добавьте таблицу «менеджер» со следующими полями:

Manager id (PK, surrogate)
Department id (FK)
Employee id (FK)
beginningDate
endingDate

где вы будете хранить историю управления: кто, для какого отдела, когда, до когда

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

РЕДАКТИРОВАТЬ:

3 — более богатое решение будет обобщением моего второго предложения и позволит вам отслеживать карьеру каждого в компании. Вы можете сделать это с помощью таблицы «works in», такой как эта (поскольку мы называем ее здесь таблицей «position», я буду использовать ту же терминологию:

Position id (PK, surrogate)
Department id (FK)
Employee id (FK)
Position Level (FK)
beginningDate
endingDate

Где «уровень должности» ведет к другой таблице, содержащей различные должности, которые могут существовать в отделе, одна из которых, конечно же, является должностью «менеджера».

Это предложение ближе к тому, что используется в базе данных и программном обеспечении HR, и вам может не понадобиться такое сложное решение. Но имейте в виду, что разделение людей на несколько таблиц ВСЕГДА является ошибкой.

РЕДАКТИРОВАТЬ: после вашего комментария...

Чтобы все было ясно, я бы посоветовал вам изменить имена полей. Я бы предложил вам иметь следующие поля:

Tbl_Employee.id_EmployeeManager

и

Tbl_Department.id_DepartmentManager

Делая это, мы (или любой разработчик) сразу поймем, что id_EmployeeManager участвует в рекурсивном отношении между людьми, а id_DepartmentManager участвует в отношении между людьми и отделом.

Вернемся к вашим вопросам, и, по моему мнению, вам не следует создавать следующую ссылку:

Tbl_Department.id_DepartmentManager -> Tbl_Employee.id_EmployeeManager

Делая это, вы имеете в виду, что кто-то не может быть руководителем отдела, если он уже не управляет сотрудниками. Как насчет отделов с одним сотрудником? Как насчет людей, назначенных руководителями только что созданного отдела, в котором еще нет ни одного работника? Это не работает. Правильная ссылка должна быть:

Tbl_Department.id_DepartmentManager -> Tbl_Employee.id_Employee

Конечно, вы можете добавить некоторые бизнес-правила, говорящие, например, что «сотрудник, управляющий отделом, может быть только менеджером» (id_Employee существует где-то как id_EmployeeManager) или «сотрудник, управляющий отделом, не может иметь менеджера (где id_EmployeeManager для этого сотрудника равен нулю). ...). Но это только бизнес-правила. Ваша модель данных может принимать все правила, если соблюдается основное правило, а именно то, что отделом управляет сотрудник!

person Philippe Grondier    schedule 02.04.2012
comment
+1 за отличное объяснение :) Но у меня есть несколько вопросов. В вашем первом (и самом простом) решении вы сказали, что я должен добавить поле идентификатора менеджера/сотрудника в таблицу отделов. Поскольку я уже добавил Manager Id в таблицу Employee для рекурсивного отношения, я должен добавить Manager Id в таблицу Department, ссылающуюся на Manager Id в таблице Employee, верно? Таким образом, только менеджер может управлять отделом, а не любой сотрудник, я думаю. - person Songo; 02.04.2012

Я думаю, что это лучшее решение:

Дизайн БД

Менеджер – это сотрудник, который руководит подразделением. Рекурсивное отношение вы можете получить следующим потоком:

У сотрудника есть отдел В отделе есть сотрудник в качестве менеджера

Возможно, удобно добавить в таблицу сотрудников столбец EmployeeType для определения роли.

person pascalvgemert    schedule 01.04.2012
comment
Manager из Department не может быть Manager из Employee. Manager для Employee является его непосредственным руководителем, а не руководителем всего отдела. Как это решить? - person Songo; 02.04.2012
comment
Если я это хорошо понимаю, то я предполагаю, давая Сотруднику отношение к сотруднику, который является менеджером. Таким образом, таблица сотрудников получает поле ManagerID, которое относится к EmployeeID. - person pascalvgemert; 03.04.2012

Мое мнение:

Таблица Person, куда вы будете добавлять информацию как для сотрудников, так и для менеджеров, менеджеры тоже люди, понимаете? :), и у вас есть поле managerId для ссылки на идентификатор менеджера.

Табличный отдел с информацией об отделе

и, если сотрудник может принадлежать более чем одному отделу, создайте таблицу employee_department, чтобы связать их. Если сотрудник может принадлежать только к одному отделу и вам не нужна дополнительная информация в отношении, добавьте полеdepartmentID в таблицу Employee.

person Diego    schedule 01.04.2012
comment
Один стол для их всех — это вроде моего второго решения, верно? По сути, вы предлагаете, чтобы таблица «Сотрудники» имела следующую структуру Employee (Employee Id, Employee Name, Manager Id, Department Id, Manager Department Id), верно? - person Songo; 02.04.2012
comment
вам не нужен идентификатор отдела менеджера, только идентификатор отдела, потому что, если сотрудник является менеджером, его отдел будет храниться в столбце DepartmentId - person Diego; 02.04.2012
comment
Но Manager из Department может не быть Manager из Employee. Manager для Employee является его непосредственным руководителем, а не руководителем всего отдела. Как это решить? - person Songo; 02.04.2012
comment
для менеджера отдела у вас будет поле ManagerId в таблице отдела, которое будет ключевым для сотрудника. Этот сотрудник может или не может быть менеджером других сотрудников - person Diego; 02.04.2012
comment
хорошо, я понимаю вашу точку зрения :) Для полноты картины, не могли бы вы сказать мне, если я хочу использовать первое решение (Manager расширяет Employee), как я могу установить рекурсивную связь между ними? Может быть, добавить столбец Manager Id в базовую таблицу Employee? - person Songo; 02.04.2012
comment
но это именно то, что я предложил - person Diego; 02.04.2012

Как насчет того, чтобы придерживаться второго дизайна и иметь псевдо-отношения?

Я предполагаю, что у вас будет столбец department_id в сущности «Сотрудник», чтобы связать отношения между сущностями «Сотрудник» и «Отдел». Если мы можем предположить, что не будет иерархии менеджеров (менеджеры менеджеров), мы можем установить псевдоотношение между двумя таблицами, где Department_ID для менеджеров (Manager_ID равно Null) представляет отдел, которым они управляют.

Пока вы четко документируете это, я думаю, что это будет экономичный подход, поскольку у вас уже будет столбец FK (department_id) в сущности «Сотрудник», ссылающийся на сущность «Отдел».

person Ellest    schedule 19.12.2017