如何最好地收集给定用户尚未阅读的邮件?
现有表格
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)
不幸的是,我不太了解执行计划。 /亚当
答案 0 :(得分:7)
建议另一种方法:
我之前遇到过完全相同的问题。我浪费了一个很好的一周,试图找到最好的方法来做到这一点。我最终创建了一个连接表,就像您所做的那样,但该表仅包含未读消息,而不是跟踪读取消息。
现在,如果我要跟踪每个人都读过的所有消息,那么数据库中的混乱会非常迅速地增长(用户 * 消息行),很容易在更小的应用中导致数千排“自重”。如果消息的生命周期是无限期的,那么这个问题就会被夸大 - 你可以跟踪多年前的消息状态。
如果跟踪反向,则“未读消息”表仅包含少量行,并且对于用户读取的每条消息,它们会减少。此外,获取未读邮件的数量就像“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
)