1

下面是我在两个包含相同列名的不同表上运行的函数。

-- 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 毫秒。

当在包含 10000 行的表上运行上述函数时,所用时间约为 27994 毫秒。

根据 1000 行的计算,10000 行的逻辑时间应该在 5360 毫秒左右,但执行时间非常长。

为了减少执行时间,请建议要进行哪些更改。

4

2 回答 2

1

根据 1000 行的计算,10000 行的逻辑时间应该在 5360 毫秒左右,但执行时间非常长。

它假定读取任何特定行与读取任何其他行所花费的时间相同,但事实并非如此。例如,如果表中有一个文本列并且它有时包含很大的内容,它将从TOAST 存储(页外)中获取并动态解压缩。

为了减少执行时间,请建议要进行哪些更改。

要读取所有表行而不需要一次在内存中获取所有行,您可以使用游标。这将避免在每次循环迭代时进行新查询。游标通过 EXECUTE 接受动态查询。

请参阅plpgsql 文档中的游标

于 2013-09-19T21:01:51.093 回答
1

据我所知,你把事情复杂化了。由于“记录计数”用于增加 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 经过优化以处理数据集,而不是进行逐行处理(这是您的循环正在执行的操作)。

您没有告诉我们您要解决的真正问题是什么(我担心您过度简化了您的示例),但鉴于问题中的代码,上面应该是一个更好的解决方案(肯定更快) .

于 2013-09-19T21:18:16.767 回答