【问题标题】:Limit Per Criteria每个标准的限制
【发布时间】:2010-09-26 21:14:34
【问题描述】:

我有一个文章表和一个类别表。我想为每个类别获取 7 篇文章。目前我有这个,但在大桌子上速度很慢,所以这不是一个真正的解决方案:

SELECT id, 
       title, 
       categories_id, 
       body, 
       DATE_FORMAT(pubdate, "%d/%m/%y %H:%i") as pubdate   
FROM articles AS t 
WHERE ( 
    SELECT COUNT(*) 
    FROM articles 
    WHERE t.categories_id = categories_id 
      AND id< t.id AND publish = 1 
      AND expires > '2008-12-14 18:38:02' 
      AND pubdate <= '2008-12-14 18:38:02' 
    ) < 7 
ORDER BY categories_id DESC

使用说明,它向我显示它正在执行连接类型 ALL 和 REF。选择类型是 PRIMARY 和 DEPENDENT SUBQUERY。

有没有更好的解决方案?

【问题讨论】:

  • 这似乎获得了少于 7 个类别的所有文章。这似乎与您对目标的英文描述略有不同。例如,如果一个类别有 10 篇文章,您的描述听起来像是您想要其中 7 篇,而不是 0 篇。

标签: sql mysql optimization greatest-n-per-group


【解决方案1】:

这是我解决这个问题的方法:

SELECT a1.id, 
       a1.title, 
       a1.categories_id, 
       a1.body, 
       DATE_FORMAT(a1.pubdate, "%d/%m/%y %H:%i") as pubdate  
FROM articles AS a1
  LEFT OUTER JOIN articles AS a2
  ON (a1.categories_id = a2.categories_id AND 
     (a1.pubdate < a2.pubdate OR (a1.pubdate = a2.pubdate AND a1.id < a2.id)))
GROUP BY a1.id
HAVING COUNT(*) < 7;

相关子查询通常表现不佳,因此该技术使用连接代替。

对于给定的文章,搜索与当前正在考虑的文章 (a1) 的类别相匹配的文章 (a2),并且具有较新的日期(或更高的 id,如果是相同的)。如果满足该标准的文章少于七篇,则当前一篇必须是其类别中最新的。

如果您可以依赖与pubdate 具有相同排序顺序的唯一id 列,那么您可以简化连接,因为在唯一列上不会有任何联系:

  ON (a1.categories_id = a2.categories_id AND a1.id < a2.id)

【讨论】:

  • 好建议,看起来是正确的做法,应该适用于所有情况并且应该很快。
【解决方案2】:
  1. 表有多大,慢到什么程度?

  2. 表上有哪些索引?

  3. EXPLAIN 的全部信息是什么?

另外,这两个日期时间值是显式的,所以看起来这要么是由由其他信息组成的东西生成的代码生成的。是否还有其他某种 SQL 查询,它在列表的循环中执行此操作?

不清楚选择了哪 7 篇文章 - 最新的?到哪一天?

【讨论】:

    【解决方案3】:

    所以看起来您要求的是少于 7 篇文章的类别;所以这就是查询应该开始的地方——

    SELECT categories_id,  COUNT(1)  
    FROM articles  
    WHERE publish = 1 
      AND expires > '2008-12-14 18:38:02' 
      AND pubdate <= '2008-12-14 18:38:02'
    GROUP BY categories_id
    HAVING COUNT(1) < 7
    

    然后将其作为子查询:

    SELECT 
        c.id, c.title, c.id, a.body,  
        DATEFORMAT(a.pubdate, "%d/%m/%y %H:%i") as pubdate  
    FROM categories c  
    JOIN articles a ON c.id = a.categories_id  
    JOIN 
    (  
        SELECT DISTINCT categories_id  
        FROM articles  
        WHERE publish = 1  
            AND expires > '2008-12-14 18:38:02'  
            AND pubdate <= '2008-12-14 18:38:02'  
        GROUP BY categories_id  
        HAVING COUNT(1) <= 7  
    ) AS j ON c.id = j.categories_id  
    ORDER BY whatever  
    

    下一步是将返回的文章数量限制为 7 - 如果这看起来合适,我可以在下一步处理这个问题。 (按原样尝试,看看 EXPLAIN 是什么样的。)

    编辑:将“

    【讨论】:

      【解决方案4】:

      您有多种选择 - 有些可能会导致性能问题,但这取决于许多因素。

      您可以将其拆分为多个查询。一个查询读取所有类别:

      SELECT categories_id FROM Categories
      

      然后对于每个类别,读出前七篇文章:

      SELECT 
        id, 
        title, 
        ...etc.
      FROM articles
      where categories_id = 1 
      

      ...每个类别以此类推。

      这样做的好处是更容易理解,但缺点是将一个查询变成 1 +(1* 猫的数量)。再说一次,你可以限制类别的数量,这样你就有了一些控制元素。有时您会发现 5 个简单的查询比 1 个复杂的查询执行得好很多!

      这种假设您正在从您可以控制的某些代码中调用 SQL - 是这样吗?

      【讨论】:

      • 我从未见过这样的情况:将带有子查询的 sql 语句分解为仅带有子查询的循环可以提高性能,除非首先出现一些可修复的错误。
      【解决方案5】:

      在测试中我发现限制 7 在 MySQL 的子查询中不起作用,请参阅 Bill 的建议,我验证它运行良好。

      SELECT id, 
             title, 
             categories_id, 
             body, 
             DATE_FORMAT(pubdate, "%d/%m/%y %H:%i") as pubdate   
      FROM articles A INNER JOIN articles B ON B.categories_ID = A.Categories_ID
      WHERE A.ID IN ( 
          SELECT ID
          FROM Articles  
          WHERE categories_id = A.categories_id 
            AND publish = 1 
            AND expires > '2008-12-14 18:38:02' 
            AND pubdate <= '2008-12-14 18:38:02' 
          LIMIT 7
          ORDER BY Categories_ID DESC) 
      ORDER BY B.Categories_ID DESC
      

      【讨论】:

      • 只有最新版本才支持 MySQL 子查询;并且优化对它们不是很好。所以这是可测试的,但需要与其他类型的方法进行比较。
      • 将 LIMIT 7 放入子查询不会限制行数 - 它只是限制子查询中要匹配的记录数。最好在子查询中选择 DISTINCT - 然后它只会查看每个类别的一行,而不是 7
      • 好点子,本来他是要每类前7行还是要少于7行的分类,最近澄清为每类最新的7篇文章。跨度>
      • 我听从比尔的智慧,这个建议的方法要好得多。
      【解决方案6】:

      虽然 Bill 的查询平均而言可能会更好一些,但单次运行需要 230 秒。我没有进行完整的测试(几次运行),但它仍然太慢,所以我想更好的选择是每个类别进行 1 次查询以获取最新的 7 项 - 看起来它会比所有其他选项更快。

      【讨论】:

      • 您是否为 WHERE 条件中的列设置了索引?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多