Мне подошла такая схема:
groups
user_id: foreign key -> users.id
manager_id: int
user_id, manager_id: primary key
is_deleted: boolean
work_id: foreign key -> works.id, nullable
worker_1_rarity: int, nullable
worker_2_rarity: int nullable
worker_3_rarity: int, nullable
users_groups_sets
id_user: foreign key -> users.id (primary key)
managers_count: int // кол-во всех управляющих
workers_1_count: int // кол-во всех рабочих 1 уровня производительности и т.д.
workers_2_count: int
workers_3_count: int
workers_4_count: int
workers_5_count: int
Т.к. группа не может быть сформирована без управляющего, то id управляющего можно сделать идентификатором бригады. А в группе надо знать, какого уровня рабочие там собраны, вот и указываем (для 3х возможных мест рабочих) уровни рабочие, которые были сформированы в группу.
Свободные группы - те, в которых work_id is null и хотя бы один из worker_X_rarity is not null.
Работающие группы - где work_id is not null.
Свободные управляющие - где все worker_X_rarity is null.
В интерфейсе для формирования групп надо получать данные о всех группах, управляющих и рабочих. Поэтому тут два запроса всего - из groups и users_groups_sets получить все записи по id пользователя. На уровне приложения уже формируем все данные: о свободных и работающих группах, о свободных управляющих и рабочих.
Удалять прямо из сформированной группы нельзя ни рабочих, ни управляющего.
Когда рабочего добавляем юзеру, то добавляем 1 к workers_X_count, где X - это уровень производительности этого рабочего.
Когда удаляем, то отнимаем 1 от нужного workers_X_count.
С добавлением управляющего, также +1 и, если есть запись с is_deleted == true, то просто у неё ставим false, иначе создаём запись в groups.
Когда удаляем, то также -1, а в groups у первой найденной записи, где все worker_X_rarity is null, ставим is_deleted в true.