直接查询表和查询返回同一张表的函数的区别

Posted

技术标签:

【中文标题】直接查询表和查询返回同一张表的函数的区别【英文标题】:Difference Between Querying Table Directly and Querying Function that Returns that Same Table 【发布时间】:2020-08-27 13:04:34 【问题描述】:

我想要一个返回 TABLE 的函数。我知道用户可以像表格一样在选择和连接中使用函数调用。但是,select/join 是否能够使用函数 TABLE 返回的源表的索引?

例如: "select id from permitted_resources() where id = 1" 会和"select id from resources where id = 5" 一样吗? (假设资源表id列有索引。)

CREATE OR REPLACE FUNCTION permitted_resources()
  RETURNS TABLE (id   int, name varchar(10)) AS
$func$
BEGIN
   RETURN QUERY
   SELECT r.id, r.name from resources r; 
END
$func$  LANGUAGE plpgsql;

【问题讨论】:

【参考方案1】:

“从 allowed_resources() where id = 1 中选择 id”是否与“从其中 id = 5 的资源中选择 id”相同?

不,它不会。 PL/pgSQL 函数是优化器的黑匣子。

如果您想实现类似的目标,请使用language sql 函数:

CREATE OR REPLACE FUNCTION permitted_resources()
  RETURNS TABLE (id   int, name varchar(10)) AS
$func$
   SELECT r.id, r.name from resources r; 
$func$  
LANGUAGE sql
stable;

我们可以使用以下设置进行测试:

create table test 
(
  id integer primary key, 
  some_nr integer default random() * 1000 + 1,
  some_date date default current_date,
  some_text text default md5(random()::text)
);

insert into test (id) 
select *
from generate_series(1,1e6);

现在创建一个 PL/pgSQL 函数:

create function get_data1()
returns setof test
as
$$
begin
 return query
   select *
   from test;
end;   
$$
language plpgsql
stable;

还有一个 SQL 函数:

create function get_data2()
returns setof test
as
$$
 select *
 from test;
$$
language sql
stable;

让我们看看执行计划的样子:

explain (analyze)
select *
from get_data1() -- this is the PL/pgSQL function
where id = 1234; 

产生以下执行计划:

Function Scan on get_data1  (cost=0.25..4.75 rows=5 width=44) (actual time=261.033..361.218 rows=1 loops=1)
  Filter: (id = 1234)
  Rows Removed by Filter: 999999
Planning Time: 0.033 ms
Execution Time: 371.302 ms

显然它首先检索所有行,然后再次丢弃它们

然而,

explain (analyze)
select *
from get_data2() -- the "SQL" function
where id = 1234; 

产生以下执行计划:

Index Scan using test_pkey on test  (cost=0.42..2.43 rows=1 width=45) (actual time=0.015..0.017 rows=1 loops=1)
  Index Cond: (id = 1234)
Planning Time: 0.119 ms
Execution Time: 0.031 ms

计划中甚至不再提及该功能。毫不奇怪,简单的选择会产生相同的计划:

explain (analyze)
select *
from test
where id = 1234;
Index Scan using test_pkey on test  (cost=0.42..2.43 rows=1 width=45) (actual time=0.014..0.014 rows=1 loops=1)
  Index Cond: (id = 1234)
Planning Time: 0.058 ms
Execution Time: 0.026 ms

我不知道这是否适用于更复杂的查询,但是这样的函数和另一个表之间的简单连接会显示相同的行为。

【讨论】:

以上是关于直接查询表和查询返回同一张表的函数的区别的主要内容,如果未能解决你的问题,请参考以下文章

子账号表的设计(不用递归实现查询,同一张表做外键)

在oracle中怎么把一张表的数据插入到另一张表中

查询同一张表时,spark sql 返回空值,但配置单元和 impaly 获取正常数据?

7-10外连接查询

更新同一张表的 JPQL 查询的性能改进

同一张表的嵌套查询