增量DISTINCT / GROUP BY操作

时间:2016-10-24 13:54:33

标签: sql postgresql greatest-n-per-group postgresql-performance

我有一个简单的两阶段SQL查询,它在两个表AB上运算,我使用子选择来检索表A的多个ID使用表B上的(可能是复杂的)查询(以及可能的其他连接表)将其作为外键存储在B中。然后,我想简单地返回x的{​​{1}}个ID。我尝试使用这样的查询:

A

这很慢,因为Postgres似乎在限制它之前对整个结果集执行SELECT sq.id FROM ( SELECT a_id AS id, created_at FROM B WHERE ... ORDER BY created_at DESC ) sq GROUP BY sq.id ORDER BY max(sq.created_at) DESC LIMIT 10; / GROUP BY操作。如果我DISTINCT子查询(例如100),表现就好了(正如我所期待的那样),但当然不再保证会有至少10个生成的LIMIT行中的a_id值不同。

同样,查询

sq

非常慢,因为Postgres似乎在SELECT a_id AS id FROM B WHERE ... GROUP BY id ORDER BY max(created_at) DESC LIMIT 10 上执行顺序扫描而不是使用(现有)索引。如果我删除了B子句,它就可以使用索引了。

GROUP BY中的数据使得大多数行包含不同的B s,因此即使没有a_id,大多数返回的ID也会不同。我通过分组追求的目标是确保结果集始终包含来自GROUP BY的给定数量的条目。

有没有办法执行"增量A / DISTINCT"?在我天真的想法中,Postgres生成结果行并将它们按递增分组直到达到GROUP BY指定的数量就足够了,在大多数情况下,它应该几乎是瞬时的,因为大多数LIMIT值都不同。我尝试了各种方法来查询数据,但到目前为止,我还没有发现任何可靠的数据。

Postgres版本是 9.6 ,数据模式如下:

a_id

2 个答案:

答案 0 :(得分:1)

规划器有机会避免对整个表进行排序的唯一方法是,如果您有完整的ORDER BY子句的索引。

然后可以选择索引扫描以获得正确的排序,并且可以快速找到前十个结果行。

答案 1 :(得分:1)

这个问题比初看起来要复杂得多。

如果......

  • 您的条件不是非常有选择性(远远超过10个a_id符合条件)
  • a_id中没有多个重复B(如您所述)

然后有一种非常快的方式。

为了简化一点,我假设created_at也定义为NOT NULL,或者你需要做更多。

WITH RECURSIVE top10 AS (
   ( -- extra parentheses required
   SELECT a_id, ARRAY[a_id] AS id_arr, created_at
   FROM   b
   WHERE  ...  -- your other filter conditions here
   ORDER  BY created_at DESC, a_id DESC  -- both NOT NULL
   LIMIT  1
   )
   UNION ALL -- UNION ALL, not UNION, since we exclude dupes a priori
   (
   SELECT b.a_id, id_arr || b.a_id, b.created_at
   FROM   top10 t
   JOIN   b ON (b.created_at, b.a_id)
             < (t.created_at, t.a_id)  -- comparing ROW values
           AND  b.a_id <> ALL (t.id_arr)
   WHERE  ... -- repeat conditions
   ORDER  BY created_at DESC, a_id DESC
   LIMIT  1
   )
   )
SELECT a_id
FROM   top10
LIMIT  10;

理想情况下,(created_at DESC, a_id DESC)上的索引支持(或仅(created_at, a_id))。

根据您的其他WHERE条件,其他(部分?)索引可能会更好。

这对于小结果集尤其有效。否则,根据其他各种细节,其他解决方案可能会更快。

相关(有更多解释):