我有这样一张桌子:
CREATE TABLE values (first_id varchar(26), sec_id int, mode varchar(6), external1_id varchar(23), external2_id varchar(26), x int, y int);
可能有多个具有相同first_id的值,我的目标是将每个first_id(所有相关行)展平(放入json)到另一个表中。
我这样做:
INSERT INTO othervalues(first_id, results)
SELECT first_id, json_agg(values) AS results FROM values GROUP BY first_id;
在结果列中,我具有所有行的json数组,以后可以按原样使用。
我的问题是,这非常慢,并且有一个巨大的表:值大约有1亿行,这使我的计算机(我实际上是在本地测试)的速度变慢,直到它消失(这是Ubuntu)。
使用EXPLAIN,我发现它是使用GroupPartitioner的,
SET work_mem = '1GB';
现在它使用了HashPartitioner,但这仍然会杀死我的计算机。一个解释给我:
Insert on othervalues (cost=2537311.89..2537316.89 rows=200 width=64)
-> Subquery Scan on "*SELECT*" (cost=2537311.89..2537316.89 rows=200 width=64)
-> HashAggregate (cost=2537311.89..2537314.39 rows=200 width=206)
-> Seq Scan on values (cost=0.00..2251654.26 rows=57131526 width=206)
任何想法如何对其进行优化?
答案 0 :(得分:0)
我最终使用的解决方案是将GROUP BY分成多个:
首先,我创建一个临时表,其中包含要分组的东西的唯一ID。这只允许获得一部分结果(例如使用OFFSET和LIMIT),但是对于庞大的结果集,这些结果可能会非常慢(较大的偏移量意味着执行树仍会浏览第一个结果):
CREATE TEMP TABLE tempids AS SELECT ROW_NUMBER() OVER (ORDER BY theid), theid FROM (SELECT DISTINCT theid FROM sourcetable) sourcetable;
然后在WHILE循环中:
DO $$DECLARE
r record;
i INTEGER := 0;
step INTEGER := 500000;
size INTEGER := (SELECT COUNT(*) FROM tempids);
BEGIN
WHILE i <= size
LOOP
INSERT INTO target(theid, theresult)
SELECT ...
WHERE tempids > i AND tempids < i + step
GROUP BY tempids.theid;
这看起来像通常的编码,这不是很好的sql,但是可以用。