【问题标题】:How should I approach the database logic behind a "forget this" feature?我应该如何处理“忘记这个”功能背后的数据库逻辑?
【发布时间】:2011-06-22 20:53:33
【问题描述】:

所以,假设我有一个 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); 之类的东西。


是否有最佳实践或一系列经验法则或(可能)比我正在采用的更好的方法来解决这个问题?

【问题讨论】:

    标签: sql ruby-on-rails postgresql


    【解决方案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;
    

    【讨论】:

    • 我知道他是根据标签要求 Postgre,但在 SQL Server 中 NOT EXISTSNOT IN 是 2008 年相同的查询计划。
    • 我不能说我以前用过NOT EXISTS,尽管我不得不说我的脑袋围绕它的语义含义有点奇怪。我会自己解决的。感谢您为我指明正确的方向。
    • @JNK:请参阅this article,了解有关 SQL Server 主题的更多讨论。
    • @JNK 实际上这取决于数据以及是否存在空值。
    【解决方案2】:

    为了获得最佳性能,您应该使用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)
    

    【讨论】:

      【解决方案3】:

      【讨论】:

      • tl;dr:那篇文章说 Postgres 为 left joinnot exists 版本生成相同的查询计划,所以唯一的区别是样式。我更喜欢left join,但这只是我。
      【解决方案4】:

      This page 对使用 LEFT OUTER JOIN 与 NOT EXISTS 进行了很好的比较。该页面上的related link 表明 NOT EXISTS 是可比的,或者比 NOT IN 更快,至少对于该博客上的示例而言。第一个链接显示 NOT EXISTS 的性能(cpu 周期和执行时间)几乎是 LEFT OUTER JOIN 的两倍,只要您对所有正在连接/匹配的列都有索引。

      ignore_pages 的索引可能如下所示:

      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
      );
      

      【讨论】:

      • 错过了 PSTGRE 标记,并错误地认为这是 TSQL。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-25
      • 1970-01-01
      • 1970-01-01
      • 2012-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多