如何解决Postgres 在非常大的分区表上加入与聚合
我有一个包含数百万行的大表。因为太大,所以先按日期范围分区,然后那个分区也按一个 period_id 分区。
CREATE TABLE research.ranks
(
security_id integer NOT NULL,period_id smallint NOT NULL,classificationtype_id smallint NOT NULL,dtz timestamp with time zone NOT NULL,create_dt timestamp with time zone NOT NULL DEFAULT Now(),update_dt timestamp with time zone NOT NULL DEFAULT Now(),rank_1 smallint,rank_2 smallint,rank_3 smallint
)
CREATE TABLE zpart.ranks_y1990 PARTITION OF research.ranks
FOR VALUES FROM ('1990-01-01 00:00:00+00') TO ('1991-01-01 00:00:00+00')
PARTITION BY LIST (period_id);
CREATE TABLE zpart.ranks_y1990p1 PARTITION OF zpart.ranks_y1990
FOR VALUES IN ('1');
每年都有一个分区,每年还有十几个分区。
我需要并排查看不同 period_ids 的 security_ids 结果。
所以我最初使用的连接是这样的:
select c1.security_id,c1.dtz,c1.rank_2 as rank_2_1,c9.rank_2 as rank_2_9
from research.ranks c1
left join research.ranks c9 on c9.dtz=c9.dtz and c1.security_id=c9.security_id and c9.period_id=9
where c1.period_id =1 and c1.dtz>Now()-interval'10 years'
这很慢,但可以接受。我将其称为 JOIN 版本。
然后,我们想再显示两个 period_ids 并扩展上述内容以在新的 period_ids 上添加额外的连接。 这减慢了连接速度,足以让我们考虑不同的解决方案。
我们发现以下类型的查询运行速度大约快 6 或 7 倍:
select c1.security_id,sum(case when c1.period_id=1 then c1.rank_2 end) as rank_2_1,sum(case when c1.period_id=9 then c1.rank_2 end) as rank_2_9,sum(case when c1.period_id=11 then c1.rank_2 end) as rank_2_11,sum(case when c1.period_id=14 then c1.rank_2 end) as rank_2_14
from research.ranks c1
where c1.period_id in (1,11,14,9) and c1.dtz>Now()-interval'10 years'
group by c1.security_id,c1.dtz;
我们可以使用总和,因为该表具有唯一索引,所以我们知道永远只会有一条记录被“求和”。我将其称为 SUM 版本。
速度快了很多,我之前写的代码有一半都被质疑了!两个问题:
-
我应该尝试在任何地方使用 SUM 版本而不是 JOIN 版本,还是效率可能是特定结构的一个因素,而在其他情况下不太可能有用?
-
SUM 版本的逻辑有没有我没有考虑的问题?
解决方法
老实说,我认为您的“加入”版本无论如何都不是一个好主意。您只有一个(分区)表,因此永远不需要任何连接。
SUM() 是可行的方法,但我会使用 SUM(...) FILTER(WHERE ..) 而不是 CASE:
SELECT
security_id,dtz,SUM(rank_2) FILTER (WHERE period_id = 1) AS rank_2_1,SUM(rank_2) FILTER (WHERE period_id = 9) AS rank_2_9,SUM(rank_2) FILTER (WHERE period_id = 11) AS rank_2_11,SUM(rank_2) FILTER (WHERE period_id = 14) AS rank_2_14,FROM
research.ranks
WHERE
period_id IN ( 1,11,14,9 )
AND dtz > now( ) - INTERVAL '10 years'
GROUP BY
security_id,dtz;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。