postgresql PL/pgSQL函数中列名、别名和OUT参数的范围

xriantvc  于 2022-11-04  发布在  PostgreSQL
关注(0)|答案(2)|浏览(315)

我很难理解为什么我可以引用returns table(col type)中的输出列。
在下面的代码中有一个微妙的错误,order by变量引用returns中的res,而不是我们别名为resdata1where中的res总是为空,我们得到0行。
为什么我可以在输出中引用列名?
在什么情况下我需要这样做?

CREATE OR REPLACE FUNCTION public.test(var INTEGER)
RETURNS table(res int )
LANGUAGE plpgsql
AS $function$
begin
    return query 
    select data1 res 
    from table_with_data 
    where res < var;
end
$function$
iugsix8n

iugsix8n1#

为什么可以在输出中引用列名

the manual开始,关于函数参数的部分:

  • column_name* RETURNS TABLE语法中输出数据栏的名称。这是宣告具名OUT参数的另一种有效方式,只是RETURNS TABLE也隐含RETURNS SETOF。

这意味着,在本例中,res实际上是一个可写变量,您计划返回一组的类型。与任何其他没有赋值默认值的变量一样,它以null开头。
在什么情况下我需要这个
您可以使用单一return query从这种类型的函数传回多笔记录,但另一种方式是使用一系列的多笔return queryreturn next-在第二种情况下,每次都要填入输出数据表之记录中的字段。您可能预期会使用return陈述式来结束函数,但是在这种情况下,只有没有添加任何东西的单个return;将具有该效果。

create table public.test_res (data integer);

CREATE OR REPLACE FUNCTION public.test(var INTEGER)
RETURNS table(res int )
LANGUAGE plpgsql
AS $function$
begin
    insert into public.test_res select res;--to inspect its initial value later
    select 1 into res;
    return next;
    return next;--note that res isn't reset after returning next
    return query select 2;--doesn't affect the current value of res
    return next;--returning something else earlier didn't affect res either
    return;--it will finish here
    select 3 into res;
    return next;
end
$function$;
select * from test(0);
-- res
-------
--   1
--   1
--   2
--   1
--(4 rows)
table public.test_res; --this was the initial value of res within the function
-- data
--------
-- null
--(1 row)

对于LOOPs,哪一项最有用?

CREATE OR REPLACE FUNCTION public.test(var INTEGER)
RETURNS table(comment text,res int) LANGUAGE plpgsql AS $function$
declare  rec record;
         array_slice int[];
begin
    return query select 'return query returned these multiple records in one go', a from generate_series(1,3,1) a(a);
    res:=0;
    comment:='loop exit when res>4';
    loop exit when res>4;
        select res+1 into res;
        return next;
    end loop;
    comment:='while res between 5 and 8 loop';
    while res between 5 and 8 loop
      select res+2 into res;
      return next;
    end loop;
    comment:='for element in reverse 3 .. -3 by 2 loop';
    for element in reverse 3 .. -3 by 2 loop
      select element into res;
      return next;
    end loop;
    comment:='for <record> in <expression> loop';
    for rec in select pid from pg_stat_activity where state<>'idle' loop
      select rec.pid into res;
      return next;
    end loop;
    comment:='foreach array_slice slice 1 in array arr loop';
    foreach array_slice SLICE 1 in array ARRAY[[1,2,3],[11,12,13],[21,22,23]] loop
      select array_slice[1] into res;
      return next;
    end loop;
end
$function$;

示例结果

select * from public.test(0);
--                        comment                         |  res
----------------------------------------------------------+--------
-- return query returned these multiple records in one go |      1
-- return query returned these multiple records in one go |      2
-- return query returned these multiple records in one go |      3
-- loop exit when res>4                                   |      1
-- loop exit when res>4                                   |      2
-- loop exit when res>4                                   |      3
-- loop exit when res>4                                   |      4
-- loop exit when res>4                                   |      5
-- while res between 5 and 8 loop                         |      7
-- while res between 5 and 8 loop                         |      9
-- for element in reverse 3 .. -3 by 2 loop               |      3
-- for element in reverse 3 .. -3 by 2 loop               |      1
-- for element in reverse 3 .. -3 by 2 loop               |     -1
-- for element in reverse 3 .. -3 by 2 loop               |     -3
-- for <record> in <expression> loop                      | 118786
-- foreach array_slice slice 1 in array arr loop          |      1
-- foreach array_slice slice 1 in array arr loop          |     11
-- foreach array_slice slice 1 in array arr loop          |     21
--(18 rows)
mfuanj7w

mfuanj7w2#

如果为True,则OUT参数(包括RETURNS TABLE (...)子句中的字段名)在PL/pgSQL函数体中的所有SQL DML语句中都可见,就像其他变量一样。有关详细信息,请参阅手册中的变量替换和从PL/pgSQL的函数返回两章。

**但是,**首先是一个更基本的误解。嵌套SELECT的语法从一开始就无效。PL/pgSQL变量恰好掩盖了此问题(具有另一个问题)。在SQL中,不能在WHERE子句中引用输出列名(SELECT子句中的列别名)。这是无效的:

select data1 res 
from table_with_data 
where res < var;

本手册:
输出列的名称可用于引用ORDER BYGROUP BY子句中的列值,但不能用于引用WHEREHAVING子句中的列值。在这里你必须写出表达式。
这与ORDER BY不同,您在文本中提到了它,但没有包括在查询中。请参阅:

  • GROUP BY + CASE语句

正在修复即时问题

可以这样修理:

CREATE OR REPLACE FUNCTION public.test1(var int)
  RETURNS TABLE(res int)
  LANGUAGE plpgsql AS
$func$
BEGIN
   RETURN QUERY
   SELECT data1 AS res     -- column alias is just noise (or documentation)
   FROM   table_with_data
   WHERE  data1 < var;     -- original column name!
END
$func$

fiddle
请参阅:

  • 三元组相似性的真实的比较

在这种情况下,列别名只是噪声。函数返回的列名在任何情况下都是res-如RETURNS TABLE子句中所定义的。
旁白:建议不要省略列别名的关键字AS(不同于表别名)。请参见:

  • ORDER BY查询从另一个SELECT返回的行数
    如果列名和变量名之间实际存在歧义(比如,您声明了一个OUT参数或名为data1的变量),则会收到如下错误消息:
ERROR:  column reference "data1" is ambiguous
LINE 2:    select data1 
                  ^
DETAIL:  It could refer to either a PL/pgSQL variable or a table column.

暴力修复

可以在函数体的开头使用特殊命令修复:

CREATE OR REPLACE FUNCTION public.test3(var int)
  RETURNS TABLE(data1 int)
  LANGUAGE plpgsql AS
$func$

# variable_conflict use_column  -- ! to resolve conflicts

BEGIN
   RETURN QUERY
   SELECT data1
   FROM   table_with_data
   WHERE  data1 < var;         -- !
END
$func$

请参阅:

正确修复

表限定列名,并避免一开始就冲突的变量名。

CREATE OR REPLACE FUNCTION public.test4(_var int)
  RETURNS TABLE(res int)
  LANGUAGE plpgsql STABLE AS
$func$
BEGIN
   RETURN QUERY
   SELECT t.data1            -- table-qualify column name
   FROM   table_with_data t
   WHERE  t.data1 < _var;    -- !
END
$func$

示例:

  • 从Java调用PostgreSQL函数

相关问题