SQL-запрос - как эффективно извлекать не прочитанные сообщения - PullRequest
3 голосов
/ 22 марта 2009

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

Существующие таблицы

Message table
----------------------------------
id    title    body    sentAt

User table
----------------------------------
id    username

Read Messages table
----------------------------------
user_id    message_id

Я думаю что-то вроде

select 
  m.id, m.title, m.sentAt, u.username
from 
  message m,
  [user] u
where 
  u.id = 1 and -- @userId parameter
  m.id not in 
    (select r.message_id from read_messages r where r.user_id = u.id)

К сожалению для меня, я не очень понимаю, глядя на план выполнения. / Адам

Ответы [ 3 ]

7 голосов
/ 22 марта 2009

Предложение альтернативного подхода:

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

Поскольку

  1. Статус-кво: «все прочитали все свои сообщения».
  2. Получение непрочитанных сообщений (или их количества) должно быть максимально быстрым.
  3. Статус-кво должен быть наименее напряженным состоянием в системе.

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

Если отслеживать обратное, ваша таблица «непрочитанных сообщений» содержит только несколько строк, и они уменьшаются для каждого сообщения, которое читает пользователь. Кроме того, получить количество непрочитанных сообщений так же просто, как «SELECT COUNT(*) FROM unread WHERE user = foo».

Но

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

Но я чувствую, что это справедливый компромисс.

YMMV, HTH.

3 голосов
/ 22 марта 2009

НЕ В это очень дорого. Вместо этого вы можете сделать что-то вроде:

SELECT
  m.id, m.title, m.sentAt
FROM
  message m
  LEFT JOIN [Read Messages] rm
    ON m.message_id = rm.message_id AND rm.user_id = @userID
WHERE
  rm.user_id IS NULL

Если у вас есть правильные индексы, это должно быть намного быстрее.

Вы получаете все сообщения и присоединяетесь к прочитанным сообщениям для этого пользователя. Затем в предложении WHERE вы просите, чтобы этот user_id был NULL для этого сообщения, то есть пользователь еще не прочитал его.

1 голос
/ 23 марта 2009

Альтернативное предложение HP, скорее всего, вам подходит. Однако, если это не так, я бы предложил использовать NOT EXISTS вместо LEFT JOIN, если это возможно в вашей среде.

По крайней мере, в MS SQL это дало бы немного более дешевый план запроса, поскольку ему не требовалась бы последняя фильтрация (user_id IS NULL)

SELECT  ...
FROM    message m
WHERE   NOT EXISTS (
    SELECT 1
    FROM    read_messages rm
    WHERE   rm.usr_id = ...
    AND rm.msg_id = m.msg_id
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...