MYSQL在由另一列排序的列中选择特定数量的重复值
Posted
技术标签:
【中文标题】MYSQL在由另一列排序的列中选择特定数量的重复值【英文标题】:MYSQL Select specific amount of duplicate values in column sorted by another column 【发布时间】:2019-01-12 00:47:27 【问题描述】:SELECT notes.* FROM notes
WHERE notes.id IN (
SELECT T1.id
FROM notes as T1
WHERE (
SELECT COUNT(*)
FROM notes as T2
WHERE T2.category_id = T1.category_id
AND T2.created_at > T1.created_at
AND T1.user_id = T2.user_id
) < N
)
AND user_id = 2
此查询在按 created_at 排序的每个 category_id 中选择 N 行。 一切正常,直到:
1) 结果数 > 50,因为性能下降是线性的或更差:即使有索引,200 行也需要 2.6 秒。
2) 结果中显示了几个相等的 created_at 值。在这种情况下,您将在类别中获得超过 N 行。
主要问题是如何优化此查询,或者可以编写另一个具有相同功能的查询?特定 user_id 的 1000 行所需的性能为 0.5 秒。 第 2 点是可选的。性能是主要问题。 http://sqlfiddle.com/#!9/aa713f/3
解释:
1 PRIMARY notes
NULL
ref PRIMARY,user_id user_id 4 const 654 100.00
NULL
1 PRIMARY T1
NULL
eq_ref PRIMARY PRIMARY 4 admin_bt.notes.id 1 100.00 Using where
3 DEPENDENT SUBQUERY T2
NULL
ref category_id,created_at,user_id,catcrbabusr catcrbabusr 4 admin_bt.T1.category_id 1148 3.33 Using where; Using index
【问题讨论】:
你能提供相同的解释 1 PRIMARY notes NULL ref PRIMARY,user_id user_id 4 const 654 100.00 NULL 1 PRIMARY T1 NULL eq_ref PRIMARY PRIMARY 4 admin_bt.notes.id 1 100.00 使用 where 3 DEPENDENT SUBQUERY T2 NULL ref category_id,created_at,user_id ,catcrbabusr catcrbabusr 4 admin_bt.T1.category_id 1148 3.33 使用where;使用索引 请相应地编辑您的问题 【参考方案1】:我不确定我是否完全理解您查询的逻辑,但以下查询至少会产生相同的结果(而且肯定会用更少的时间):
这假设在 (user_id, category_id,created_at) 上有一个索引
SELECT x.*
FROM notes x
JOIN
( SELECT user_id
, category_id
, MAX(created_at) created_at
FROM notes
WHERE user_id IN(2)
GROUP
BY user_id
, category_id
) y
ON y.user_id = x.user_id
AND y.category_id = x.category_id
AND y.created_at = x.created_at;
这是您可能想尝试的另一个想法...
SELECT id
, created_at
, user_id
, category_id
FROM
( SELECT x.*
, CASE WHEN @prev = category_id THEN @i:=@i+1 ELSE @i:=1 END i
, @prev := category_id
FROM notes x
, (SELECT @prev:=null,@i:=0) vars
WHERE user_id = 2
ORDER
BY category_id
, created_at
) n
WHERE i <= 2;
【讨论】:
看不到在哪里可以设置每个类别的行数。我认为这是因为没有这样对我很重要的功能。可能是我没有很好地解释这一点。我的查询中的 N 是结果中应显示的每个类别的行数。对于 N = 1,我的查询将产生与您相同的结果,只是慢得多。您可以在我的 sqlfiddle 链接中设置 N = 2 并看到响应中有 2 行 category_id = 6。您的查询在 N = 1 时仍然可能有用,因为性能超出了任何预期。感谢您的回复。 所以最终,您会想要一个返回给定用户每个类别的前 n 行最近的行的查询? user_id 只是一个例子。范围无关紧要,因为我可以弄清楚如何在任何查询中设置它。 category_id 和 created_at 是重要的部分,因为我不需要每个类别的随机 N 行,而只需要基于 created_at 的第一个或最后一个。 第二个查询速度很快,工作正常,甚至在某种程度上(尚未正确测试)解决了每个类别超过 N 个结果且 created_at 值相等的问题。这就是答案。谢谢。【参考方案2】:相关查询自然很昂贵,尤其是在遍历大量第一级行时。它们通常不适合 OLTP。它们可以用于批处理。
如果您需要向在线页面显示此响应,那么也许您应该将结果缓存在您的应用程序中并定期刷新(每 10 分钟一次?)。
无论如何,即使有索引,这个查询也有可能会随着表的增长而变得越来越慢。
另一种选择是定期预处理子查询并将其存储在表中。然后查询它。此查询适用于快速回复。我不知道 mysql 是否有它,但 PostgreSQL 有“物化视图”,就是为了这个目的(可以按需刷新)。
【讨论】:
(mysql 目前没有)以上是关于MYSQL在由另一列排序的列中选择特定数量的重复值的主要内容,如果未能解决你的问题,请参考以下文章
使用 dplyr [重复] 有条件地将一列中的值替换为另一列中的值