我应该如何处理“忘记这个”功能背后的数据库逻辑?

时间:2011-06-22 20:53:34

标签: sql ruby-on-rails postgresql

所以,我们说我有一个users表和一个pages表。让我们说我想让用户隐藏/忘记/忽略某些页面。在这一点上,我可以想到两种可能的方法:

null匹配的外部联接:

我可以创建一个单独的ignored_pages表,列user_idpage_id,当ID为1的用户忽略ID为2的页面时写入INSERT INTO ignored_pages (user_id, page_id) VALUES (1,2);

然后我可以运行像SELECT pages.* FROM pages LEFT OUTER JOIN ignored_pages ON pages.id = ignored_pages.page_id WHERE ignored_pages.user_id = 1 AND ignored_pages.id IS NULL;

这样的东西

编辑: Joe Stefanelli指出我的查询中有错误。它应该是SELECT pages.* FROM pages LEFT OUTER JOIN ignored_pages ON pages.id = ignored_pages.page_id AND ignored_pages.user_id = 1 WHERE ignored_pages.id IS NULL;

子查询和NOT IN

我可以使用相同的多对多表,然后运行类似SELECT pages.* FROM pages WHERE page_id NOT IN (SELECT page_id FROM ignored_pages WHERE user_id = 1);的内容。


是否有最佳做法或一系列经验法则或(可能)比我所采取的方法更好地解决这个问题?

4 个答案:

答案 0 :(得分:1)

您的最佳表现实际上可能是NOT EXISTS

SELECT p.* 
    FROM pages p
    WHERE NOT EXISTS(SELECT 1 
                         FROM ignored_pages 
                         WHERE user_id = 1 
                             AND page_id = p.id);

如果您决定坚持使用LEFT JOIN选项,则需要更正该查询以在连接条件而不是user_id子句上测试WHERE

SELECT pages.* 
    FROM pages 
        LEFT OUTER JOIN ignored_pages 
            ON pages.id = ignored_pages.page_id 
                AND ignored_pages.user_id = 1 
    WHERE ignored_pages.id IS NULL;

答案 1 :(得分:1)

为获得最佳效果,您应使用NOT EXISTS

SELECT pages.* 
FROM pages
WHERE NOT EXISTS(
   SELECT NULL
   FROM ignored_pages
   WHERE user_id = 1 AND ignored_pages.page_id = pages.page_id)

答案 2 :(得分:1)

答案 3 :(得分:0)

This page对使用LEFT OUTER JOIN和NOT EXISTS进行了很好的比较。该页面上的related link显示NOT EXISTS与NOT IN相比或更快,至少对于该博客上的示例案例而言。第一个链接显示NOT EXISTS的执行速度几乎是LEFT OUTER JOIN的两倍(cpu周期和执行时间),只要你有一个索引就可以加入/匹配所有列。

ignored_pa​​ges的索引可能如下所示:

CREATE UNIQUE CLUSTERED INDEX IX_Ignored_Pages ON ignored_pages (user_id, page_id);

适应您的代码,NOT EXISTS语法看起来像这样:

SELECT p.*
FROM pages p
WHERE NOT EXISTS (
   SELECT 1
   FROM ignored_pages i
   WHERE i.user_id = @user_id
   AND i.page_id = p.page_id
);