我有一个SQL函数重新调整复合结果。
CREATE TYPE result_t AS (a int, b int, c int, d numeric); CREATE OR REPLACE FUNCTION slow_function(int) RETURNS result_t AS $$ -- just some placeholder code to make it slow SELECT 0, 0, 0, ( SELECT sum(ln(i::numeric)) FROM generate_series(1, $1) i ) $$ LANGUAGE sql IMMUTABLE;
调用函数时,我希望将复合类型的各个部分扩展为几列。当我打电话时,这很好用:
SELECT (slow_function(i)).* FROM generate_series(0, 200) i a b c d ---- ---- ---- -------------------- 0 0 0 (null) 0 0 0 0 0 0 0 0.6931471805599453 0 0 0 1.791759469228055 ... Total runtime: 6196.754 ms
不幸的是,这将导致 每个结果列 仅调用 一次 该函数,这会不必要地降低速度。可以通过将运行时间与查询进行比较来进行测试,查询可以直接返回复合结果并以四倍的速度运行:
SELECT slow_function(i) FROM generate_series(0, 200) i ... Total runtime: 1561.476 ms
示例代码也位于http://sqlfiddle.com/#!15/703ba/7
如何在不浪费CPU能力的情况下获得多列结果?
甚至不需要CTE。一个 普通的子查询 也可以完成这项工作(已在9.3版中进行了测试):
SELECT i, (f).* -- decompose here FROM ( SELECT i, (slow_func(i)) AS f -- do not decompose here FROM generate_series(1, 3) i ) sub;
确保不要在子查询中分解函数的复合结果。将其保留给 外部查询 。 当然需要一个众所周知的类型。无法使用匿名记录。
或者说,什么@Richard写道,一个 LATERAL JOIN 作品了。语法可以更简单:
LATERAL JOIN
SELECT * FROM generate_series(1, 3) i, slow_func(i) f
LATERAL
FROM
SQL Fiddle 具有EXPLAIN VERBOSE所有变体的输出。如果发生该功能,您可以 看到 对该功能的多次评估。
EXPLAIN VERBOSE
COST
通常,(对于此特定查询无关紧要),请确保对您的函数应用高成本设置,因此计划者会知道避免进行更多必要的评估。喜欢:
CREATE OR REPLACE FUNCTION slow_function(int) RETURNS result_t AS $func$ -- expensive body $func$ LANGUAGE sql IMMUTABLE **COST 100000** ;
每个文档:
较大的值会使计划人员尝试避免不必要地评估函数。