Каков наилучший способ хранения отношений «один ко многим» или «многие ко многим» в PostgreSQL? - PullRequest
4 голосов
/ 24 ноября 2011

В настоящее время я интегрирую чат с открытым исходным кодом (AJAX Chat) в другой проект.Теперь чат по умолчанию просто получает действительных пользователей и действительные каналы из файла, но, очевидно, это не идеально, если у вас есть база данных с постоянно меняющимися пользователями.

Итак, я хочу сделать так, чтобычат загружает информацию о пользователе и канале прямо из базы данных.Я думал, что дизайн должен быть следующим (Если вы чувствуете по-другому, пожалуйста, дайте мне знать):

  • У нас есть несколько каналов чата (общедоступный, маркетинг и т. Д ...)
  • Затем у нас есть группы, назначенные каналам (например, пиар-команда 1, ИТ-специалисты и т. Д.).
  • Затем у нас есть пользователи, которые входят в группы И в некоторых случаях назначаются непосредственно каналам.

Я думал о реализации вышеизложенного с помощью таблиц, подобных этим:

Таблица каналов:

|----|Channel_Name||Channel_ID||Groups_Assigned||Users_Assigned|----|  
|----|---Public---||-----0----||---1,2,3,4,5---||-----3,4------|----|  
.  
.  
.etc...

Примечание: Таблица назначенных групп содержит идентификаторы групп:группы, назначенные каналу, в то время как назначенные пользователи содержат идентификатор пользователей, которые не являются частью назначенных групп.

Таблица групп:

|----|Group_Name||Group_ID||Users_Assigned|----|  
|----|---Team1--||----0---||------5,10----|----|  
.  
.  
.etc...  

Приносим извинения за плохо нарисованные таблицы.

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

Моя идея возможна, но кажется немного неэффективной.Поскольку мне нужно было бы хранить назначенные идентификаторы (как группы, так и пользователя) в формате 1,2,3...., мне пришлось бы использовать либо PHP explode(), либо какую-либо другую функцию PostgreSQL, которая может искать строки.Скорее всего, я буду хранить массив групп, а затем циклически повторять их все, по одной строке за раз, это мне кажется очень медленным.

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

Итак, как бы вы, ребята, сделали это?И если по какой-то безумной причине вы согласитесь с моей первоначальной идеей, то не могли бы вы помочь мне понять, как на самом деле написать код, чтобы на самом деле это сделать.

Спасибо за ваше время, хорошегодень.

Ответы [ 3 ]

6 голосов
/ 24 ноября 2011

Да, неэффективно хранить разделенные запятыми строки чисел и пытаться найти в вашей базе данных заданное число. Подробнее об этом см. Мой ответ на Действительно ли плохое хранение списка, разделенного запятыми, в столбце базы данных?

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

2 голосов
/ 24 ноября 2011

Я бы выбрал еще одну таблицу вместо значений 1,2,3,4,5, поскольку их трудно читать. Удалите Groups_Assigned из таблицы channels и поместите ее в отдельную таблицу в формате 1 ко многим:

Channel_id  Group_id
----------  --------
0           1
0           2
0           3
0           4
0           5

Я бы создал еще одну таблицу в виде групп, которая присоединялась бы к этой таблице здесь и содержит информацию о том, что представляет собой каждый group_id. Тогда нужно писать запросы, способные читать этот дизайн так, как вам нужно.

1 голос
/ 25 ноября 2011

Одно из возможных решений:

Channel
------------
Channel_Id
Channel_Name
PRIMARY KEY (Channel_Id)

Person и Grouping (я предпочитаю те, которые выше User и Group, так как некоторые системы используют их в качестве ключевых слов) можно рассматривать как подтипысупертип Entity.Это поможет позже иметь только одну Assignment таблицу.

Entity
------------
Entity_Id
PRIMARY KEY (Entity_Id)

Person  --- ( User )
------------
Person_Id
Person_Name
--- other data about persons/users
PRIMARY KEY (Person_Id)
FOREIGN KEY (Person_Id)
  REFERENCES Entity(Entity_Id)

Grouping   --- ( Group )
------------
Grouping_Id
Grouping_Name
--- other data about groups
PRIMARY KEY (Grouping_Id)
FOREIGN KEY (Grouping_Id)
  REFERENCES Entity(Entity_Id)

Это будет использоваться для Person - Grouping ассоциации:

Belongs --- ( Person Belongs In Grouping )
------------
Person_Id
Grouping_Id
PRIMARY KEY (Person_Id, Grouping_Id)
FOREIGN KEY (Person_Id)
  REFERENCES Person(Person_Id)
FOREIGN KEY (Grouping_Id)
  REFERENCES Grouping(Grouping_Id)

и таблицы ассоциации для назначений каналам

Assignment ( Entity is Assigned to Channel )
------------
Entity_Id
Channel_Id
PRIMARY KEY (Entity_Id, Channel_Id)
FOREIGN KEY (Entity_Id)
  REFERENCES Entity(Entity_Id)
FOREIGN KEY (Channel_Id)
  REFERENCES Channel(Channel_Id)

Вы можете, конечно, избавиться от таблицы Entity и иметь две таблицы ассоциаций, одну для Person to Channel и одну для Group to Channel назначений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...