【问题标题】:PostgreSQL: ERROR: $name$ is not a scalar variablePostgreSQL:错误:$name$ 不是标量变量
【发布时间】:2012-08-25 11:14:32
【问题描述】:

有一个函数返回3个参数,其中一个是复合类型:

CREATE OR REPLACE FUNCTION f1(
  p_text text,
  OUT result_status_id smallint,
  OUT result_status_msg text,
  OUT result_my_type my_type
  )
  RETURNS record AS
$BODY$
--body here
$BODY$
LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER
  COST 100

复合类型 my_type 如下所示:

CREATE TYPE my_type AS
   (d_real real,
    d_int1 integer,
    d_int2 integer,
    d_int3 integer,
    d_int4 integer,
    d_int5 integer,
    d_int6 integer,
    d_int7 integer,
    d_int8 integer,
    d_int9 integer,
    d_int10 integer,
    d_bool boolean,
    d_date date,
    d_text text
);

还有另一个函数 f2 在其主体中调用函数 f1:

CREATE OR REPLACE FUNCTION f2(
    p_text text
)
  RETURNS record AS
$BODY$
DECLARE
    l_status_id smallint;
    l_status_msg text;
    l_my_type my_type;
BEGIN
--some logic here

--this statement fails
  SELECT * FROM f1(p_text) 'x' 
  INTO l_status_id, l_status_msg, l_my_type;

--logic continues here
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER
  COST 100;

问题是使用函数执行sql时收到以下错误:

ERROR: "l_my_type" is not a scalar variable

如何从另一个函数中获取复合类型对象?

【问题讨论】:

    标签: function postgresql stored-procedures syntax-error plpgsql


    【解决方案1】:

    在您的 f2() 函数中,您可以对记录变量进行选择,然后从中提取您需要的内容,例如:

    CREATE OR REPLACE FUNCTION f2(p_text text )
      RETURNS record AS
    $BODY$
    DECLARE
        record_var record;
    BEGIN
    --this statement was failing
      SELECT * FROM f1(p_text) INTO record_var;
    
     SELECT l_my_type.result_my_type.d_int1; -- this should work
    
    END;
    $BODY$
      LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER
      COST 100;       
    

    【讨论】:

      【解决方案2】:

      元组中的复合类型是部分受支持的东西之一,您会遇到奇怪的问题(尤其是像存储这样的精简,但这是另一个故事)。您可以做到这一点的一种方法是:

      CREATE TYPE return_type_for_function AS (
             result_status_id smallint,
             result_status_msg text,
             result_my_type my_type
      );
      
      CREATE FUNCTION myfunc(....) RETURNS return_type_for_function ....
      

      这是我一直这样做的方式。这比使用 OUT 变量要成熟一点。

      这是一个简单的例子:

      or_examples=# create table rel_examples.tabletest (id int);
      CREATE TABLE
      
      or_examples=# create table comp_table_test (test rel_examples.tabletest);
      CREATE TABLE
      
      or_examples=# create function test(int) returns comp_table_test
      immutable language sql as $$
      select row(row($1))::comp_table_test; $$;
      CREATE FUNCTION
      or_examples=# select test(1);
        test   
      ---------
      ("(1)")
      (1 row)
      

      【讨论】:

      • +1 是一个可接受的解决方案,但我发现了你可能在我的答案中找到的技巧
      【解决方案3】:

      然而,@Chris Travers 提出了一个可接受的解决方案,在我的情况下,很遗憾无法引入一种新类型来从 f1 函数返回数据。

      尽管如此,可以在函数 f2() 中调用函数 f1() 并仍然使用以下语法获取数据:

      CREATE OR REPLACE FUNCTION f2(
          p_text text
      )
        RETURNS record AS
      $BODY$
      DECLARE
          l_status_id smallint;
          l_status_msg text;
          l_my_type my_type;
          l_record record;
      BEGIN
      --some logic here
      
      --this statement is okay now
        l_record = f1(p_text);
        l_status_id = l_record.result_status_id;
        l_status_msg = l_record.result_status_msg;
        l_my_type = l_record.result_my_type;
      
      --logic continues here
      END;
      $BODY$
        LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER
        COST 100;
      

      【讨论】:

      • 这是更简单的形式,是的。另一方面:plpgsql 中的赋值运算符是:== 运算符未记录在案。考虑一下related question
      【解决方案4】:

      你违反了规则。 The manual:

      其中 target 可以是记录变量、行变量或 简单变量和记录/行字段的逗号分隔列表。

      记录或行变量不能成为多项目INTO 列表的一部分。
      一种解决方法:

      CREATE OR REPLACE FUNCTION f2(p_text text)
        RETURNS record AS
      $BODY$
      DECLARE
         r            record;
         l_status_id  smallint;
         l_status_msg text;
         l_my_type    my_type;
      BEGIN
         SELECT *
         FROM f1(p_text) x  -- don't single quote 'x'
         INTO r;
      
         l_status_id  := r.result_status_id;
         l_status_msg := r.result_status_msg;
         l_my_type    := r.result_my_type;
      
        RETURN r; -- or whatever ..
      END;
      $BODY$
        LANGUAGE plpgsql VOLATILE;
      

      还有更多方法。这取决于你要去哪里。我几乎从不返回匿名记录。

      【讨论】:

      • 这也可以,并且在 x 附近没有单引号。也没有别名函数
      • 重要的部分是对您遇到的错误的解释(为您公众)。如前所述,有多种方法可以解决它。在您自己发布了简单的作业后,我选择了一个不同的示例。
      猜你喜欢
      • 2017-08-05
      • 1970-01-01
      • 1970-01-01
      • 2011-08-11
      • 1970-01-01
      • 2018-05-02
      • 1970-01-01
      • 2013-08-02
      • 1970-01-01
      相关资源
      最近更新 更多