3

我有一个文章表和一个类别表。我想为每个类别获取 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 。

有更好的解决方案吗?

4

6 回答 6

5

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

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在相同的情况下更高的日期)。如果符合该标准的文章少于七篇,则当前一篇必须是其类别中最新的。

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

  ON (a1.categories_id = a2.categories_id AND a1.id < a2.id)
于 2008-12-14T22:52:05.207 回答
2
  1. 桌子有多大,慢有多慢?

  2. 表上有哪些索引?

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

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

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

于 2008-12-14T20:30:51.437 回答
1

所以看起来你是在要求那些少于 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 的样子。)

编辑:将“< 7”更改为 <= 7”

于 2008-12-14T21:32:18.083 回答
0

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

于 2008-12-15T02:33:24.543 回答
0

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

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

SELECT categories_id FROM Categories

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

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

...等等每个类别。

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

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

于 2008-12-14T20:21:49.457 回答
0

在测试中我发现限制 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
于 2008-12-14T20:36:40.613 回答