разработка схемы базы данных системы обмена сообщениями - PullRequest
39 голосов
/ 01 июля 2011

Я пытаюсь добиться именно того, что объяснено здесь: Создание многопоточной системы личных сообщений, такой как Facebook и Gmail , однако я не совсем понимаю ответ Джоэла Брауна.Может ли кто-нибудь объяснить, пожалуйста.

Вот так выглядят мои таблицы БД с образцами данных (я полагаю, я правильно их заполнил для демонстрационных целей): enter image description here

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

  2. Мне нужно отобразить ВСЕсообщения в ветке сообщений (LINQ) -> точно так же, как это делается на Facebook, где вы нажимаете на сообщение, и вы видите весь «разговор» в протекторе.

Пожалуйста, помогите!спасибо

РЕДАКТИРОВАТЬ -> продолжение Джоэл, это правильно ??

enter image description here

Джоэл, я немного запутался, можете ли выпожалуйста, объясните (комментарии / вопросы, выделенные жирным шрифтом):

Идея заключается в том, что каждый раз, когда пользователь запускает новую ветку / сообщение, он начинается с новой записи в таблице THREAD.Затем пользователь добавляется как THREAD_PARTICIPANT, а содержимое сообщения добавляется в MESSAGE, который указывает на содержащую THREAD.FK от СООБЩЕНИЯ до ПОЛЬЗОВАТЕЛЯ указывает автора сообщения.

LoginId 1 отправляет сообщение в LoginId2 => новая запись вставляется в таблицу MessageThread.Также в запись MessageThreadParticipant вставляется запись с MessageThreadId = 1, LoginId = 1 (отправитель).И новая запись вставляется в таблицу сообщений с MessageId = 1, MessageThreadid = 1, SenderLoginId = 1 (правильно ??)

это то, что у меня есть после этой итерации: enter image description here

Мне кажется, я запутался, потому что у Loginid 2 нет возможности узнать, что для него есть сообщение.??ИЛИ может мне нужно вставить 2 записи в MessageThreadParticipant ??(отправитель и получатель) -> таким образом оба могут видеть весь «разговор» ??

EDIT2: Джо, думаю, я мог бы сделать это:

SELECT
  Message.MessageId, Message.CreateDate, Message.Body, Login.Username, Message.SenderLoginId
, (SELECT MessageReadState.ReadDate 
   FROM MessageReadState 
   WHERE MessageReadState.MessageId = Message.MessageId 
     ) as ReadDate
FROM Message 
    INNER JOIN Login ON Message.SenderLoginId = Login.LoginId
    INNER JOIN MessageThreadParticipant mtp on mtp.MessageThreadId = Message.MessageThreadId 
AND ( Message.MessageId in 
        ( SELECT Max(Message.MessageId)
          FROM MessageThreadParticipant INNER JOIN Message 
            ON MessageThreadParticipant.MessageThreadId = Message.MessageThreadId
          GROUP BY MessageThreadParticipant.MessageThreadId
        )
      )
Where mtp.LoginId = 2
ORDER BY Message.CreateDate DESC;

Пожалуйста, поправьте меня, если я ошибаюсь:)

Ответы [ 2 ]

72 голосов
/ 01 июля 2011

Ну, почему бы тебе просто не спросить? :)

Позвольте мне попытаться понять ваше требование. Мне кажется, что вы смотрите на поток, представляющий собой линейный список (а не дерево) сообщений между двумя людьми. Я думаю, что вы можете позволить больше людей, чем просто два. Это было бы похоже на Facebook, поскольку кто-то публикует сообщение, и тогда любое количество людей может прочитать его, а затем начать добавлять комментарии. Когда вы добавляете комментарий, он попадает в цепочку, и вы начинаете получать обновления статуса и электронные письма, рассказывающие об активности в цепочке и так далее. Если предположить, что это то, что вам нужно, то схема, которую я предложил Big Mike , не совсем то, что вы ищете.

Вместо этого рассмотрим следующее:

Schema

Идея заключается в том, что каждый раз, когда пользователь запускает новую ветку / сообщение, он начинается с новой записи в таблице THREAD. Затем пользователь добавляется как THREAD_PARTICIPANT, а содержимое сообщения добавляется в MESSAGE, который указывает на содержащую THREAD. ФК от СООБЩЕНИЯ до ПОЛЬЗОВАТЕЛЯ указывает автора сообщения.

Когда пользователь читает сообщение, он получает запись в таблице MESSAGE_READ_STATE, указывающую, что он пометил сообщение как прочитанное, явно или неявно, в зависимости от того, как идут ваши требования.

Когда кто-то комментирует начальное сообщение в цепочке, второе СООБЩЕНИЕ добавляется с FK обратно в исходную THREAD, и автор ответа (пользователь) добавляется в таблицу THREAD_PARTICIPANT. И так происходит, когда сообщения добавляются в ветку одним, двумя или более участниками.

Чтобы получить самое последнее сообщение в любой цепочке, просто возьмите верхнюю 1 из отсортированного СООБЩЕНИЯ по убыванию в дату создания (или идентификационный ключ), где сообщение FK соответствует интересующей нити.

Чтобы получить самую последнюю обновленную ветку для пользователя, получите THREAD, относящуюся к первой 1, из отсортированного по убыванию сообщения на дату создания, когда сообщение находится в потоке, в котором пользователь является THREAD_PARTICIPANT.

Боюсь, я никогда не смогу заявить об этом в LINQ, не разбив LinqPad. Если у вас возникли проблемы с обнаружением моего отклонения от вышесказанного, я мог бы уточнить ответ с помощью определений таблиц и некоторого SQL. Просто спросите в комментариях.

РЕДАКТИРОВАТЬ: Разъяснение требований и реализации

Разъяснение требований: Изначально я думал о публично опубликованных сообщениях с возможностью комментирования, тогда как Шейн больше интересуется функцией прямых сообщений. В этом случае первоначальный получатель должен быть включен в таблицу THREAD_PARTICIPANT с самого начала.

Для ясности, давайте поместим несколько строк в таблицы. Вот сценарий, (в честь Дня Канады): Пользователь 1 DM Пользователь 2, чтобы спросить о встрече для пива. Пользователь 2 отвечает вопросом о том, где встретиться, и Пользователь 1 отвечает. Таблицы будут выглядеть примерно так: (вероятно, упрощенно)

Sample Data Part 1 Sample Data Part 2

РЕДАКТИРОВАТЬ # 2: Доступ к SQL для списка всех сообщений в потоке, с состоянием чтения ...

Используя схему @ OP, этот SQL получит список сообщений в заданном потоке с указанием, прочитал ли данный пользователь каждое сообщение или нет. Сообщения в первом порядке.

SELECT 
  Message.MessageId
, Message.CreateDate
, Message.Body
, Login.Username
, (SELECT MessageReadState.ReadDate 
   FROM MessageReadState 
   WHERE MessageReadState.MessageId = Message.MessageId 
     and MessageReadState.LoginId = 2) as ReadState
FROM (Message INNER JOIN Login ON Message.SenderLoginId = Login.LoginId) 
WHERE (((Message.MessageThreadId)=10))
ORDER BY Message.CreateDate DESC;

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

РЕДАКТИРОВАТЬ 3: SQL для получения всех потоков с последним сообщением в каждом для данного пользователя ...

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

SELECT
  Message.MessageId
, Message.CreateDate
, Message.Body
, Login.Username
, (SELECT MessageReadState.ReadDate 
   FROM MessageReadState 
   WHERE MessageReadState.MessageId = Message.MessageId 
     and MessageReadState.LoginId = 2) AS ReadState
FROM Message INNER JOIN Login ON Message.SenderLoginId = Login.LoginId
WHERE ( Message.MessageId in 
        ( SELECT Max(Message.MessageId)
          FROM MessageThreadParticipant INNER JOIN Message 
            ON MessageThreadParticipant.MessageThreadId = Message.MessageThreadId
          WHERE MessageThreadParticipant.LoginId=2
          GROUP BY MessageThreadParticipant.MessageThreadId
        )
      )
ORDER BY Message.CreateDate DESC;
2 голосов
/ 10 сентября 2013

Согласно Joel Brown'answer, вы можете добавить столбец LAST_MESSAGE_ID в таблицу THREAD, тогда получение всех потоков с последними сообщениями SQL станет очень простым. Вы должны обновить этот столбец при отправке каждого сообщения.

Получение всех тем с последним сообщением в каждом для данного пользователя

SELECT *
FROM THREAD T
INNER JOIN MESSAGE M ON T.LAST_MESSAGE_ID=M.MESSAGE_ID
INNER JOIN USER SENDER ON M.USER_ID=SENDER.USER_ID
LEFT JOIN MessageReadState MRS ON M.MESSAGE_ID=MRS.MESSAGE_ID AND MRS.USER_ID=2
...