我有一个非常昂贵的plsql函数的查询,它执行了大量的递归计算:
SELECT a, b, expensive_recursion(c, d, e) FROM my_table
函数expensive_recursion()有这样的plpgsql:
CREATE OR REPLACE FUNCTION expensive_recursion(col varchar, parent varchar, date varchar)
RETURNS integer AS
$BODY$
DECLARE
_value integer;
_rec record;
BEGIN
-- get the value for the current parent that matches the range
EXECUTE 'SELECT value_'||col||' FROM foo WHERE foo.id = '||quote_literal(parent)||' AND foo.range = '||quote_literal(range) INTO _value;
-- get all children of parent and sum their values
FOR rec IN EXECUTE 'SELECT child FROM relationships WHERE parent = '||quote_literal(parent) LOOP
_value = _value + expensive_recursion(col, rec.child, range);
END LOOP;
RETURN _value;
END;
$BODY$
LANGUAGE plpgsql STABLE COST 1000;
虽然这有效,但我想不仅为当前行进行缓存,而是为当前查询中的所有行启用缓存,并在查询完全执行后清除缓存。
例如,原始函数带有一些伪代码: 创建或替换函数expensive_recursion(col varchar,parent varchar,range varchar) RETURNS整数AS $ BODY $ 宣布 _value整数; _rec记录; BEGIN
-- pseudo-code to check the cache and get the cached value or insert into cache
IF parent AND range IN cache
RETURN SELECT value FROM cache WHERE cache.parent = parent and cache.range = range;
END IF;
-- get the value for the current parent that matches the range
EXECUTE 'SELECT value_'||col||' FROM foo WHERE foo.id = '||quote_literal(parent)||' AND foo.range = '||quote_literal(range) INTO _value;
-- get all children of parent and sum their values
FOR rec IN EXECUTE 'SELECT child FROM relationships WHERE parent = '||quote_literal(parent) LOOP
_value = _value + expensive_recursion(col, rec.child, range);
END LOOP;
-- put new value in cache
INSERT INTO cache (value, parent, range) VALUES (_value, parent, range);
-- final value with sum for parent and its children
RETURN _value;
END;
$BODY$
LANGUAGE plpgsql STABLE COST 1000;
有没有办法做到这一点并创建/删除缓存?我可以使用临时表或者我在plpgsql函数中管理的某些结构/类型。诀窍是多个用户可以执行此功能,并且出于特定于应用程序的原因,不能跨查询共享缓存。
答案 0 :(得分:1)
您可以像普通表一样创建临时表;相应地符合条件:
create temporary table ( … );
http://www.postgresql.org/docs/current/static/sql-createtable.html
这样做有微小的下行空间和潜在的重大上行空间。缺点是,如果临时集很小,那么您正在创建并随后删除目录中的行,并在流程中添加死行,然后需要对其进行清理。好处是,如果临时集很大,你可以在它上面创建一个索引并最终analyze
表,然后继续查询它。
您可以控制是在事务结束时还是在会话结束时使用on commit
在其定义中删除临时表。
除此之外,执行递归查询的另一种方法是使用CTE,也称为查询:
http://www.postgresql.org/docs/current/static/queries-with.html
对于所有意图和目的,它创建一个匿名(和非索引)临时表。你可以递归地做到这一点,这样做可以相当有效。
Methinks尝试更改您的查询和基础逻辑,以便首先尝试使用CTE。如果所有其他方法都失败了,那么请考虑实际使用临时表。