我有一个简单的两阶段SQL查询,它在两个表A
和B
上运算,我使用子选择来检索表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
答案 0 :(得分:1)
规划器有机会避免对整个表进行排序的唯一方法是,如果您有完整的ORDER BY
子句的索引。
然后可以选择索引扫描以获得正确的排序,并且可以快速找到前十个结果行。
答案 1 :(得分:1)
这个问题比初看起来要复杂得多。
如果......
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
条件,其他(部分?)索引可能会更好。
这对于小结果集尤其有效。否则,根据其他各种细节,其他解决方案可能会更快。
相关(有更多解释):