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
答案 0 :(得分:0)
相关查询自然很昂贵,尤其是在遍历大量第一行时。它们通常不适合OLTP。它们可以进行批处理。
如果您需要在在线页面上显示此响应,那么也许您应该将结果缓存在您的应用程序中并定期刷新(每10分钟一次?)。
无论如何,即使有索引,表的增长也可能使查询变得越来越慢。
另一种选择是定期预处理子查询并将其存储在表中。然后查询它。该查询将适用于快速响应。我不知道MySQL是否拥有它,但是PostgreSQL具有“物化视图”,仅用于此目的(可以按需刷新)。
答案 1 :(得分:0)
我不确定我是否完全了解您查询的逻辑,但是以下查询至少会产生相同的结果(并且肯定会花费更少的时间):
这假设在(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;