获取每组前n条记录的优化方法
Optimized way to get top n records of each group
我需要 mysql table 每个子类别的前 6 条记录,几乎有 10 万条记录。我尝试了以下 mysql 查询,但我担心它在我的 table 中有大量记录的性能。
SELECT
*
FROM
(SELECT
sub_cat_id,
title,
@rn:=IF(@prev = sub_cat_id, @rn + 1, 1) AS rn,
@prev:=sub_cat_id AS previd,
created_date
FROM
blog
WHERE
type = 'BLOG'
AND FIND_IN_SET(sub_cat_id, '1,2,8')
AND created_date <= NOW()
ORDER BY sub_cat_id DESC , created_date DESC) AS records
WHERE
rn <= 6
在上面的查询中,MySQL 将对具有 sub_cat_id 1,2 和 8 的所有记录进行编号。在外部 select 查询中,记录将减少为 6 条记录对于每个子类别。
我有以下问题
- 这是为每个子类别获取 6 条记录的更好最快的方法吗
- 这个查询是否生成了想要的结果。
这是我的博客table
+-------- +------------+-------+--------------+
| blog_id | sub_cat_id | title | created_date |
+-------- +------------+-------+--------------+
| 1 | 1 | ABC | 2018-05-25 |
| 2 | 1 | ABC | 2018-05-22 |
| 3 | 2 | ABC | 2018-05-23 |
| 4 | 2 | ABC | 2018-05-21 |
| 5 | 2 | ABC | 2018-05-20 |
| 6 | 8 | ABC | 2018-05-15 |
+-------- +------------+-------+--------------+
你的方法很好,但你的查询不行。特别是,MySQL 不保证 SELECT
中表达式的求值顺序,因此您不应在一个表达式中分配变量并在另一个表达式中使用它。
幸运的是,您可以将赋值组合成一个表达式:
SELECT b.*
FROM (SELECT b.sub_cat_id, b.title, created_date
(@rn := IF(@sc = b.sub_cat_id, @rn + 1,
if(@sc := b.sub_cat_id, 1, 1)
)
) as rn
FROM blog b CROSS JOIN
(SELECT @sc := -1, @rn := 0) params
WHERE b.type = 'BLOG' AND
b.sub_cat_id IN (1, 2, 8) AND
b.created_date <= NOW() -- is this really needed?
ORDER BY b.sub_cat_id DESC, b.created_date DESC) AS records
) b
WHERE rn <= 6;
对于此查询,您需要索引。我认为这会起作用:type, sub_cat_id, created_date)
。不幸的是,group by
仍然需要对数据进行排序。在 MySQL 的最新版本中,我认为您需要在子查询中进行排序,然后再进行 rn
赋值。
我想知道是否可以使这个公式更有效:
select b.*
from blogs b
where b.type = 'BLOG' and
b.sub_cat_id in (1, 2, 8) and
b.created_at >= (select b2.created_at
from blogs b2
where b2.type = b.type and
b2.sub_cat_id = b.sub_cat_id
order by b2.created_at desc
limit 1 offset 5
);
为此,您需要 blog(type, sub_cat_id, created_at)
上的索引。
我需要 mysql table 每个子类别的前 6 条记录,几乎有 10 万条记录。我尝试了以下 mysql 查询,但我担心它在我的 table 中有大量记录的性能。
SELECT
*
FROM
(SELECT
sub_cat_id,
title,
@rn:=IF(@prev = sub_cat_id, @rn + 1, 1) AS rn,
@prev:=sub_cat_id AS previd,
created_date
FROM
blog
WHERE
type = 'BLOG'
AND FIND_IN_SET(sub_cat_id, '1,2,8')
AND created_date <= NOW()
ORDER BY sub_cat_id DESC , created_date DESC) AS records
WHERE
rn <= 6
在上面的查询中,MySQL 将对具有 sub_cat_id 1,2 和 8 的所有记录进行编号。在外部 select 查询中,记录将减少为 6 条记录对于每个子类别。
我有以下问题
- 这是为每个子类别获取 6 条记录的更好最快的方法吗
- 这个查询是否生成了想要的结果。
这是我的博客table
+-------- +------------+-------+--------------+
| blog_id | sub_cat_id | title | created_date |
+-------- +------------+-------+--------------+
| 1 | 1 | ABC | 2018-05-25 |
| 2 | 1 | ABC | 2018-05-22 |
| 3 | 2 | ABC | 2018-05-23 |
| 4 | 2 | ABC | 2018-05-21 |
| 5 | 2 | ABC | 2018-05-20 |
| 6 | 8 | ABC | 2018-05-15 |
+-------- +------------+-------+--------------+
你的方法很好,但你的查询不行。特别是,MySQL 不保证 SELECT
中表达式的求值顺序,因此您不应在一个表达式中分配变量并在另一个表达式中使用它。
幸运的是,您可以将赋值组合成一个表达式:
SELECT b.*
FROM (SELECT b.sub_cat_id, b.title, created_date
(@rn := IF(@sc = b.sub_cat_id, @rn + 1,
if(@sc := b.sub_cat_id, 1, 1)
)
) as rn
FROM blog b CROSS JOIN
(SELECT @sc := -1, @rn := 0) params
WHERE b.type = 'BLOG' AND
b.sub_cat_id IN (1, 2, 8) AND
b.created_date <= NOW() -- is this really needed?
ORDER BY b.sub_cat_id DESC, b.created_date DESC) AS records
) b
WHERE rn <= 6;
对于此查询,您需要索引。我认为这会起作用:type, sub_cat_id, created_date)
。不幸的是,group by
仍然需要对数据进行排序。在 MySQL 的最新版本中,我认为您需要在子查询中进行排序,然后再进行 rn
赋值。
我想知道是否可以使这个公式更有效:
select b.*
from blogs b
where b.type = 'BLOG' and
b.sub_cat_id in (1, 2, 8) and
b.created_at >= (select b2.created_at
from blogs b2
where b2.type = b.type and
b2.sub_cat_id = b.sub_cat_id
order by b2.created_at desc
limit 1 offset 5
);
为此,您需要 blog(type, sub_cat_id, created_at)
上的索引。