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

时间:2015-12-01 11:57:42

标签: postgresql function variables 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}');

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

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
$$;

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

select func('test');
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

1 个答案:

答案 0 :(得分:17)

核心误解:record变量包含单个行(或为NULL),而不是表(0-n行的已知类型)。有 no"表变量"在Postgres或PL / pgSQL 。根据任务的不同,有多种选择:

因此,您无法将 多个 行分配给record类型变量。在这个声明中:

EXECUTE format('SELECT id, tags FROM %s', _tbl) INTO t;

... 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;

致电(请注意语法!)

SELECT * FROM func('test');

相关:

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

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

但是我完全取代了它,在EXECUTE之后立即执行此操作会更便宜:

GET DIAGNOSTICS _ct = ROW_COUNT; 

Details in the manual.

为什么ANALYZE

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