Я решил это с помощью таблицы двойного назначения:
Таблица: groupMemberships
id
groupID
linktype
linkID
linktype
может быть либо текстовым полем («пользователь», «группа»), которое приятно и читаемо (если вы выполняете собственный SQL-доступ, это может стоить небольшого снижения производительности), либо вы можете использовать Поле INT, где (1,2) где каждый для вас что-то значит.
Чтобы найти всех пользователей в группе 2:
SELECT linkID as userID FROM groupMemberships
WHERE groupID=2 AND linktype='user';
Чтобы найти все подгруппы:
SELECT linkID as groupID FROM groupMemberships
WHERE groupID=2 AND linktype='group';
Одна из проблем с такой двойной таблицей заключается в том, что вы хотите - сказать - получить всех пользователей группы, но не знаете, сколько их подгрупп может быть. Если вы можете быть уверены, что подгруппы будут иметь только один уровень:
SELECT linkID as userID FROM groupMemberships
WHERE groupID=2 and linktype='user'
UNION
SELECT sg.linkID as userID FROM groupMemberships g,groupMemberships sg
WHERE g.groupID=2 AND g.linktype='group'
AND sg.groupID=g.linkID AND sg.linktype='user'
Если у вас есть два уровня подгруппы, вам понадобятся два союза и так далее. Когда у вас есть n
подгруппы, вам нужно N союзов, что очень сложно писать и неэффективно.
Альтернативное решение - сохранить таблицы contacts
и groups
одинаковыми и просто связать контакты с группами - таблица contactsGroups
ID
groupID
userID
Теперь сторона кода (вне базы данных), когда вы добавляете пользователя в группу (windplayers), вы также автоматически добавляете его в любые родительские группы (оркестр), чтобы пользователь фактически получил две записи в таблице contactsGroups
. Здесь хранится та же самая информация, но она значительно облегчает поиск данных. Вы даже можете сохранить эту информацию подгруппы в базе данных, хотя она уже есть на стороне кода с наследованием объектов или чем-то подобным.