原来 SQL 函数是可以内联的!( 二 )


• 如果函数被声明为RETURNS SETOF RECORD且没有OUT参数,则函数体查询结果的类型,必须与调用方提供的列定义列表匹配
请注意,这些条件允许函数体是一个复杂的查询 , 甚至带有 CTE 表达式(但不包括递归 CTE)的查询 。这使得可内联的 SQL 函数比视图更加强大,因为它们可以以视图无法做到的方式获取参数,同时保留了视图的许多优点 。
示例让我们开始设置一个小的测试用例:
CREATE TABLE t (id integer, str text);INSERT INTO t (id, str)SELECT i, 'xxx'FROM generate_series(1, 10000) AS s(i);下面是一个可由优化器内联的函数示例:
CREATE OR REPLACE FUNCTION ld(int)RETURNS numeric AS $$SELECT log(2, $1);$$ LANGUAGE 'sql' IMMUTABLE;这是一个标记为IMMUTABLE的普通 SQL 函数 。它是可以被优化器进行优化的 。为了简单起见,该函数所做的只是计算一个对数:
SELECT ld(1024);ld--------------------- 10.0000000000000000(1 row)如您所见,该函数工作符合预期 。
为了演示事情是如何运作的,我们在函数上创建了一个索引:
CREATE INDEX idx_ld ON t (ld(id));正如预期的那样,该索引会像任何其他索引一样被使用 。但是 , 让我们来仔细看看索引条件:
EXPLAIN SELECT * FROM t WHERE ld(id) = 10;QUERY PLAN------------------------------------------------------------------------ Bitmap Heap Scan on t(cost=4.67..30.55 rows=50 width=8)Recheck Cond: (log('2'::numeric, (id)::numeric) = '10'::numeric)->Bitmap Index Scan on idx_ld(cost=0.00..4.66 rows=50 width=0)Index Cond: (log('2'::numeric, (id)::numeric) = '10'::numeric)(4 rows)ANALYZE t;EXPLAIN SELECT * FROM t WHERE ld(id) = 10;QUERY PLAN------------------------------------------------------------------ Index Scan using idx_ld on t(cost=0.29..8.30 rows=1 width=8)Index Cond: (log('2'::numeric, (id)::numeric) = '10'::numeric)(2 rows)这里重要的观察结果是 , 索引条件实际上查找的是 log 函数而不是 ld 函数 。优化器已完全消除了函数调用 。还值得一提的是,更新的优化器统计信息对于生成有效的计划非常重要 。
逻辑上讲,这为以下查询打开了优化的大门:
EXPLAIN SELECT * FROM t WHERE log(2, id) = 10;QUERY PLAN------------------------------------------------------------------ Index Scan using idx_ld on t(cost=0.29..8.30 rows=1 width=8)Index Cond: (log('2'::numeric, (id)::numeric) = '10'::numeric)(2 rows)优化器设法内联了该函数 , 并为我们提供了一个索引扫描,这远远优于高开销的顺序操作 。




推荐阅读