下面是我在两个包含相同列名的不同表上运行的函数。
-- Function: test(character varying)
-- DROP FUNCTION test(character varying);
CREATE OR REPLACE FUNCTION test(table_name character varying)
RETURNS SETOF void AS
$BODY$
DECLARE
recordcount integer;
j integer;
hstoredata hstore;
BEGIN
recordcount:=getTableName(table_name);
FOR j IN 1..recordcount LOOP
RAISE NOTICE 'RECORD NUMBER IS: %',j;
EXECUTE format('SELECT hstore(t) FROM datas.%I t WHERE id = $1', table_name) USING j INTO hstoredata;
RAISE NOTICE 'hstoredata: %', hstoredata;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
当在包含1000行的表上运行上述函数时,时间约为536 ms。
当在包含10000行的表上运行上述函数时,所用时间约为27994 ms。
根据1000行的计算,10000行的逻辑时间应该在5360 ms左右,但执行时间非常长。
为了减少执行时间,请建议做出哪些更改。
答案 0 :(得分:1)
逻辑上10000行的时间应该是5360毫秒左右 从1000行计算,但执行时间非常长。
它假定读取任何特定行与读取任何其他行的时间相同,但事实并非如此。 例如,如果表中有一个文本列,并且它有时包含大量内容,则会从TOAST storage(页面外)获取并动态解压缩。
为了减少执行时间,请建议要做出哪些更改 制成。
要读取所有表行而不必一次读取所有内存,可以使用游标。这将避免每次循环迭代时的新查询。游标通过EXECUTE接受动态查询。
请参阅plpgsql文档中的Cursors。
答案 1 :(得分:1)
据我所知,你的事情过于复杂。由于“recordcount”用于递增ID值,我认为您可以使用单个语句执行所有操作,而不是分别查询每个ID。
CREATE OR REPLACE FUNCTION test(table_name varchar)
RETURNS void AS
$BODY$
DECLARE
rec record;
begin
for rec in execute format ('select id, hstore(t) as hs from datas.%I', table_name) loop
RAISE NOTICE 'RECORD NUMBER IS: %',rec.id;
RAISE NOTICE 'hstoredata: %', rec.hs;
end loop;
end;
$BODY$
language plpgsql;
唯一与您的解决方案不同的是,如果不存在小于表中行数的ID,则不会看到RECORD NUMBER
消息。但是你会看到比表格的行数更大的ID。
任何时候你在一个循环中一次又一次地执行相同的声明非常非常响亮的警报响应应该在你的脑袋里响起。 SQL经过优化处理数据集,而不是逐行处理(这就是你的循环正在做的事情)。
您没有告诉我们您要解决的真正的问题是什么(我担心您过度简化了您的示例)但是根据问题的代码,上面应该是一个更好的解决方案(绝对很多更快)。