Дизайн базы данных для рекурсивных отношений - PullRequest
20 голосов
/ 01 апреля 2012

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

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

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

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

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

First Solution

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

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

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


Второй раствор:

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

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

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


Вопросы:

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

Ответы [ 5 ]

25 голосов
/ 02 апреля 2012

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

enter image description here

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

  • Менеджер "наследует" сотрудника.
    • Чтобы представить сотрудника, вставьте одну строку в EMPLOYEE.
    • Чтобы представить менеджера, вставьте одну строку в EMPLOYEE и одну строку в MANAGER.
  • В отделе может быть несколько сотрудников.
  • В каждом отделе ровно 1 менеджер, а каждый менеджер управляет 0 или 1 отделом.
  • Руководитель может быть обычным работником или менеджером.
  • Отделы не обязаны "совпадать":
    • Руководитель может работать в отделе, отличном от контролируемого сотрудника.
    • Менеджер может управлять другим отделом, из которого он (-ы) работает.
    • Если руководитель является руководителем, то отдел (-ы), которым он управляет, отдел (-ы), в котором он работает, и отдел (-ы) его / ее подчиненных сотрудников могут отличаться.

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


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

enter image description here

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

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


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

enter image description here

... это обозначает "категорию". В этом контексте вы можете просто интерпретировать это как отношение «от 1 до 0 или 1» между EMPLOYEE и MANAGER.

1 голос
/ 02 апреля 2012

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

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

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

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

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

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

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

EDIT:

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 сотрудник является нулевым ...). Но это только бизнес-правила. Ваша модель данных может принять все правила при условии соблюдения основного правила, а именно, что отделом управляет сотрудник!

0 голосов
/ 19 декабря 2017

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

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

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

0 голосов
/ 01 апреля 2012

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

DB Design

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

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

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

0 голосов
/ 01 апреля 2012

Мое мнение:

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

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

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

...