SQL查询 - 如何有效地获取非读取消息

时间:2009-03-22 00:33:51

标签: sql

如何最好地收集给定用户尚未阅读的邮件?

现有表格

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 个答案:

答案 0 :(得分:7)

建议另一种方法:

我之前遇到过完全相同的问题。我浪费了一个很好的一周,试图找到最好的方法来做到这一点。我最终创建了一个连接表,就像您所做的那样,但该表仅包含未读消息,而不是跟踪读取消息。

因为

  1. 现状是“每个人都阅读了他们所有的信息”。
  2. 获取未读消息(或其计数)应该尽可能快。
  3. 现状应该是系统中最不紧张的状态。
  4. 现在,如果我要跟踪每个人都读过的所有消息,那么数据库中的混乱会非常迅速地增长(用户 * 消息行),很容易在更小的应用中导致数千排“自重”。如果消息的生命周期是无限期的,那么这个问题就会被夸大 - 你可以跟踪多年前的消息状态。

    如果跟踪反向,则“未读消息”表仅包含少量行,并且对于用户读取的每条消息,它们会减少。此外,获取未读邮件的数量就像“SELECT COUNT(*) FROM unread WHERE user = foo”一样简单。

    但是

    作为一切,这是一种权衡。虽然阅读速度与计算速度一样快,但写作是一件苦差事。对于每个书面消息,您需要在此连接表中插入一个条目。此外,如果多个人可以阅读相同的邮件,则需要为每个收件人插入一行。如果收件人是隐式的(例如,只提供用户组的名称,或者甚至是“有权访问此事物的任何人”等标准),则创建新邮件会变得更加复杂。

    但我觉得这是一个公平的妥协。

    YMMV,HTH。

答案 1 :(得分:3)

NOT IN非常昂贵。相反,你可以做类似的事情:

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

如果你有正确的索引,这应该快得多。

您正在获取该用户的所有消息和LEFT JOINing读取消息。然后,在WHERE子句中,您要求该消息的user_id为NULL,这意味着用户尚未读取它。

答案 2 :(得分:1)

惠普的替代建议很可能适合你。但是,如果不是,Id建议使用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
)