带有OUT参数的PostgreSQL函数和带有TABLE结果的函数之间有任何形式上的区别吗



考虑这两个PostgreSQL函数:

CREATE OR REPLACE FUNCTION f_1 (v1 INTEGER, v2 OUT INTEGER)
AS $$
BEGIN
    v2 := v1;
END
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION f_2 (v1 INTEGER)
RETURNS TABLE(v2 INTEGER)
AS $$
BEGIN
    v2 := v1;
END
$$ LANGUAGE plpgsql;

在任何"普通"过程SQL语言(例如Transact-SQL(中,这两种类型的函数都会大不相同。f_1实际上是一个过程,而f_2则是一个表值函数。在SQL Server中,后者是从INFORMATION_SCHEMA.ROUTINES返回的,如下所示:

SELECT r.routine_schema, r.routine_name
FROM   information_schema.routines r
WHERE  r.routine_type = 'FUNCTION'
AND    r.data_type = 'TABLE'

然而,在PostgreSQL中,这是行不通的。以下查询显示f_1f_2的签名基本上没有区别:

SELECT r.routine_name, r.data_type, p.parameter_name, p.data_type
FROM   information_schema.routines r
JOIN   information_schema.parameters p
USING (specific_catalog, specific_schema, specific_name);

以上收益率:

routine_name | data_type | parameter_name | data_type
-------------+-----------+----------------+----------
f_1          | integer   | v1             | integer
f_1          | integer   | v2             | integer
f_2          | integer   | v1             | integer
f_2          | integer   | v2             | integer

当我从函数中返回多个列时,情况并没有好转,在这种情况下,我甚至不再有"正式"的返回类型。仅record:

CREATE OR REPLACE FUNCTION f_3 (v1 INTEGER, v2 OUT INTEGER, v3 OUT INTEGER)
AS $$
BEGIN
    v2 := v1;
END
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION f_4 (v1 INTEGER)
RETURNS TABLE(v2 INTEGER, v3 INTEGER)
AS $$
BEGIN
    v2 := v1;
END
$$ LANGUAGE plpgsql;

我会得到:

routine_name | data_type | parameter_name | data_type
-------------+-----------+----------------+----------
f_3          | record    | v1             | integer
f_3          | record    | v2             | integer
f_3          | record    | v3             | integer
f_4          | record    | v1             | integer
f_4          | record    | v2             | integer
f_4          | record    | v3             | integer

如果来自其他数据库,显然词汇签名的意图是完全不同的。作为一名Oracle人员,我希望PROCEDURES有副作用,而FUNCTIONS没有任何副作用(除非在自主事务中(,并且可以安全地嵌入SQL中。我知道PostgreSQL聪明地将所有函数都视为表,但我认为在任何查询中将OUT参数设计为表列不是一个好主意。。。

我的问题是:

这两种声明函数的方法在形式上有什么区别吗?如果有,我如何从INFORMATION_SCHEMAPG_CATALOG中发现它?

df public.f_*执行

select
    n.nspname as "Schema",
    p.proname as "Name",
    pg_catalog.pg_get_function_result(p.oid) as "Result data type",
    pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types",
    case
        when p.proisagg then 'agg'
        when p.proiswindow then 'window'
        when p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype then 'trigger'
        else 'normal'
    end as "Type"
from
    pg_catalog.pg_proc p
    left join
    pg_catalog.pg_namespace n on n.oid = p.pronamespace
where
    p.proname ~ '^(f_.*)$'
    and n.nspname ~ '^(public)$'
order by 1, 2, 4;

它返回这个

                                          List of functions
 Schema | Name |       Result data type        |            Argument data types             |  Type  
--------+------+-------------------------------+--------------------------------------------+--------
 public | f_1  | integer                       | v1 integer, OUT v2 integer                 | normal
 public | f_2  | TABLE(v2 integer)             | v1 integer                                 | normal
 public | f_3  | record                        | v1 integer, OUT v2 integer, OUT v3 integer | normal
 public | f_4  | TABLE(v2 integer, v3 integer) | v1 integer                                 | normal
(4 rows)

要删除函数,必须传递其输入(ININOUT(参数数据类型。然后我猜函数名称及其输入参数数据类型确实形成了它的签名。要更改返回的数据类型,必须首先将其删除并重新创建。

pg_catalog.pg_proc.proretset标志包含一个关于函数是否返回集合(即表(的提示

SELECT   r.routine_name, r.data_type, p.parameter_name, 
         p.data_type, pg_p.proretset
FROM     information_schema.routines r
JOIN     information_schema.parameters p
USING   (specific_catalog, specific_schema, specific_name)
JOIN     pg_namespace pg_n
ON       r.specific_schema = pg_n.nspname
JOIN     pg_proc pg_p
ON       pg_p.pronamespace = pg_n.oid
AND      pg_p.proname = r.routine_name
WHERE    r.routine_schema = 'public'
AND      r.routine_name IN ('f_1', 'f_2', 'f_3', 'f_4')
ORDER BY routine_name, parameter_name;

以上将产生:

routine_name | data_type | parameter_name | data_type | proretset
-------------+-----------+----------------+-----------+----------
f_1          | record    | v1             | integer   | f
f_1          | record    | v2             | integer   | f
f_2          | record    | v1             | integer   | t
f_2          | record    | v2             | integer   | t
f_3          | record    | v1             | integer   | f
f_3          | record    | v2             | integer   | f
f_3          | record    | v3             | integer   | f
f_4          | record    | v1             | integer   | t
f_4          | record    | v2             | integer   | t
f_4          | record    | v3             | integer   | t

INFORMATION_SCHEMA.COLUMNS仿真

值得一提的是,如果有人需要这个疯狂的东西,下面是我为模拟SQL Server漂亮的INFORMATION_SCHEMA.COLUMNS实现而提出的漂亮查询,该实现返回表值函数列(这是我们在jOOQ的代码生成器中支持表值函数时真正需要的(:

SELECT 
  p.proname AS TABLE_NAME,
  columns.proargname AS COLUMN_NAME, 
  ROW_NUMBER() OVER(PARTITION BY p.oid ORDER BY o.ordinal) AS ORDINAL_POSITION,
  format_type(t.oid, t.typtypmod) AS DATA_TYPE,
  information_schema._pg_char_max_length(t.oid, t.typtypmod) AS CHARACTER_MAXIMUM_LENGTH,
  information_schema._pg_numeric_precision(t.oid, t.typtypmod) AS NUMERIC_PRECISION,
  information_schema._pg_numeric_scale(t.oid,t.typtypmod) AS NUMERIC_SCALE,
  not(t.typnotnull) AS IS_NULLABLE
FROM pg_proc p,
LATERAL generate_series(1, array_length(p.proargmodes, 1)) o(ordinal),
LATERAL (
  SELECT 
    p.proargnames[o.ordinal], 
    p.proargmodes[o.ordinal], 
    p.proallargtypes[o.ordinal]
) columns(proargname, proargmode, proargtype),
LATERAL (
  SELECT pg_type.oid oid, pg_type.* 
  FROM pg_type 
  WHERE pg_type.oid = columns.proargtype
) t
WHERE p.proretset
AND proargmode = 't'
AND p.proname LIKE 'f%';

上面很好地返回(列名缩写为SO(:

table_name | column_name  | ordinal | data_type | length | precision | scale | nullable
f_2        | v2           |       1 | integer   |        |        32 |     0 | t
f_4        | v2           |       1 | integer   |        |        32 |     0 | t
f_4        | v3           |       2 | integer   |        |        32 |     0 | t

RETURNS TABLE()OUT参数与RETURNS SETOF ...的组合实际上相同如果没有额外的SETOF关键字,具有OUT参数的函数总是返回单行,而具有RETURNS TABLE()的函数可以返回0-n行

你的例子只是碰巧没有表现出差异,因为它是如何写的。

这反映在系统目录CCD_ 24的标志CCD_。手册:

函数返回一组(即指定数据类型的多个值(

信息模式视图information_schema.routines在这方面不是特别有帮助。这是一种淡化的标准化折衷,以独立于平台的形式呈现信息,很难在Postgres中描述细节。

函数签名

手册:

如果两个函数具有相同的名称,并且输入参数类型,忽略任何OUT参数

其中";输入";参数类型包括CCD_ 27和CCD_。

这两个函数在处理函数定义时非常有用。手册:

pg_get_function_arguments(func_oid) ... get argument list of function's definition (with default values)
pg_get_function_identity_arguments(func_oid) ... get argument list to identify a function (without default values)

更多相关答案:

  • 如何获取函数参数列表(这样我就可以删除函数(
  • DROP FUNCTION而不知道参数的数量/类型

最新更新