如何在plpgsql中使用记录类型变量?

SG.*_*shi 16 postgresql variables function plpgsql table-variable

如何将存储在记录类型变量中的查询结果用于同一存储函数中的另一个查询?我使用Postgres 9.4.4.

用这样的表:

create table test (id int, tags text[]);
insert into test values (1,'{a,b,c}'),
                        (2,'{c,d,e}');
Run Code Online (Sandbox Code Playgroud)

我写了一个函数(简化),如下所示:

CREATE OR REPLACE FUNCTION func(_tbl regclass)
RETURNS TABLE (t TEXT[], e TEXT[])
LANGUAGE plpgsql AS $$
DECLARE
  t RECORD;
  c INT;
BEGIN
  EXECUTE format('SELECT id, tags FROM %s', _tbl) INTO t;
  SELECT count(*) FROM t INTO c;
  RAISE NOTICE '% results', c;
  SELECT * FROM t;
END
$$;
Run Code Online (Sandbox Code Playgroud)

......但没有奏效:

select func('test');
Run Code Online (Sandbox Code Playgroud)
ERROR:  42P01: relation "t" does not exist
LINE 1: SELECT count(*) FROM t
                             ^
QUERY:  SELECT count(*) FROM t
CONTEXT:  PL/pgSQL function func(regclass) line 7 at SQL statement
LOCATION:  parserOpenTable, parse_relation.c:986
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 18

芯误解:一个record变量保存一个单个行(或是NULL),而不是一个表(公知类型的0-n行).Postgres或PL/pgSQL没有"表变量".根据任务的不同,有各种替代方案:

因此,您不能将行分配给record类型变量.在这个声明中:

EXECUTE format('SELECT id, tags FROM %s', _tbl) INTO t;
Run Code Online (Sandbox Code Playgroud)

... Postgres 分配第一行并丢弃其余部分.由于"第一个"在您的查询中没有很好地定义,因此您最终会选择一个任意选择.显然是由于一开始就提到的误解.

一个record变量也可以不到位SQL查询表的使用.这是导致错误的主要原因:

关系"t"不存在

现在应该很清楚,count(*)开始时没有任何意义,因为t只是一个记录/行 - 除此之外不可能.

最后(即使其余部分都有效),你的返回类型似乎错了:(t TEXT[], e TEXT[]).因为你选择id, tagst,你想要返回类似的东西(id int, e TEXT[]).

你想要做的是这样的:

CREATE OR REPLACE FUNCTION func(_tbl regclass)
  RETURNS TABLE (id int, e text[]) AS
$func$
DECLARE
   _ct int;
BEGIN
   EXECUTE format(
      'CREATE TEMP TABLE tmp ON COMMIT DROP AS
       SELECT id, tags FROM %s'
    , _tbl);

   GET DIAGNOSTICS _ct = ROW_COUNT; -- cheaper than another count(*)

   -- ANALYZE tmp;  -- if you are going to run multiple queries

   RAISE NOTICE '% results', _ct;

   RETURN QUERY TABLE tmp;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

调用(注意语法!):

SELECT * FROM func('test');
Run Code Online (Sandbox Code Playgroud)

有关:

只是一个概念证明.在选择整个表时,您只需使用基础表.实际上你WHERE在查询中会有一些条款......

小心潜伏类型不匹配,count()返回bigint,您无法将其分配给integer变量.需要演员:count(*)::int.

但是我完全取代了它,之后运行它会更便宜EXECUTE:

GET DIAGNOSTICS _ct = ROW_COUNT; 
Run Code Online (Sandbox Code Playgroud)

手册中的详细信息.

为什么ANALYZE


另外:普通SQL中的CTE通常可以完成这项工作:

  • 非常感谢你,欧文,友好而详细的回答。现在原因我很清楚了。为此,我将使用 CTE。 (2认同)