On this page
37.4. 查询语言(SQL)函数
SQL 函数执行 SQL 语句的任意列表,并返回列表中最后一个查询的结果。在简单(未设置)的情况下,将返回上一个查询结果的第一行。 (请记住,除非使用ORDER BY
,否则多行结果的“第一行”定义不明确.)如果最后一个查询恰好不返回任何行,则将返回空值。
或者,可以通过将函数的返回类型指定为SETOF sometype
或等效地通过将其声明为RETURNS TABLE(columns)
来声明 SQL 函数返回一个集(即多行)。在这种情况下,将返回上一个查询结果的所有行。详细信息如下。
SQL 函数的主体必须是用分号分隔的 SQL 语句列表。最后一条语句后的分号是可选的。除非声明该函数返回void
,否则最后一条语句必须是SELECT
或具有RETURNING
子句的INSERT
,UPDATE
或DELETE
。
SQL 语言中的任何命令集合都可以打包在一起并定义为一个函数。除了SELECT
查询,这些命令还可以包括数据修改查询(INSERT
,UPDATE
和DELETE
)以及其他 SQL 命令。 (在 SQL 函数中不能使用事务控制命令,例如COMMIT
,SAVEPOINT
和某些 Util 命令例如VACUUM
.)但是,最终命令必须是SELECT
或具有RETURNING
子句,该子句可以返回指定为函数返回类型的任何内容。或者,如果要定义一个执行操作但没有有用值返回的 SQL 函数,则可以将其定义为返回void
。例如,此函数从emp
表中删除薪水为负的行:
CREATE FUNCTION clean_emp() RETURNS void AS '
DELETE FROM emp
WHERE salary < 0;
' LANGUAGE SQL;
SELECT clean_emp();
clean_emp
-----------
(1 row)
Note
SQL 函数的整个主体在执行任何函数之前都会被分析。虽然 SQL 函数可以包含更改系统目录的命令(例如CREATE TABLE
),但是在对函数中的后续命令进行语法分析时,这些命令的效果将不可见。因此,例如,如果将CREATE TABLE foo (...); INSERT INTO foo VALUES(...);
打包到单个 SQL 函数中,它将无法按预期工作,因为在解析INSERT
命令时foo
尚不存在。在这种情况下,建议使用 PL/pgSQL 代替 SQL 函数。
CREATE FUNCTION
命令的语法要求将函数主体编写为字符串常量。通常,对字符串常量使用美元引号(请参见Section 4.1.2.4)最方便。如果选择使用常规的单引号字符串常量语法,则必须在函数主体中将单引号('
)和反斜杠(\
)(假定转义字符串语法)加倍(请参见Section 4.1.2.1)。
37 .4.1. SQL 函数的参数
可以使用名称或数字在函数主体中引用 SQL 函数的参数。这两种方法的示例如下所示。
要使用名称,请将函数参数声明为具有名称,然后只需将该名称写在函数主体中即可。如果参数名称与函数中当前 SQL 命令中的任何列名称相同,则列名称优先。要覆盖此参数,请使用函数本身的名称(即function_name.argument_name
)限定参数名称。 (如果这将与限定的列名冲突,则列名再次获胜.您可以通过在 SQL 命令中为表选择其他别名来避免歧义.)
在较旧的数字方法中,使用语法$n
引用参数:$1
引用第一个 Importing 参数,$2
引用第二个 Importing 参数,依此类推。无论是否使用名称声明了特定参数,这都将起作用。
如果参数是复合类型,则点符号(例如argname.fieldname
或$1.fieldname
)可用于访问参数的属性。同样,您可能需要用函数名来限定参数名,以使带有参数名的表格变得清晰。
SQL 函数参数只能用作数据值,不能用作标识符。因此,例如,这是合理的:
INSERT INTO mytable VALUES ($1);
但这不起作用:
INSERT INTO $1 VALUES (42);
Note
PostgreSQL 9.2 中增加了使用名称来引用 SQL 函数参数的功能。在较旧的服务器中使用的功能必须使用$n
表示法。
37 .4.2. 基本类型的 SQL 函数
可能的最简单的 SQL 函数没有参数,仅返回基本类型,例如integer
:
CREATE FUNCTION one() RETURNS integer AS $$
SELECT 1 AS result;
$$ LANGUAGE SQL;
-- Alternative syntax for string literal:
CREATE FUNCTION one() RETURNS integer AS '
SELECT 1 AS result;
' LANGUAGE SQL;
SELECT one();
one
-----
1
请注意,我们在函数体内为函数的结果定义了一个列别名(名称为result
),但是该列别名在函数外部不可见。因此,结果标记为one
而不是result
。
定义以基本类型作为参数的 SQL 函数几乎一样容易:
CREATE FUNCTION add_em(x integer, y integer) RETURNS integer AS $$
SELECT x + y;
$$ LANGUAGE SQL;
SELECT add_em(1, 2) AS answer;
answer
--------
3
另外,我们可以省去参数的名称并使用数字:
CREATE FUNCTION add_em(integer, integer) RETURNS integer AS $$
SELECT $1 + $2;
$$ LANGUAGE SQL;
SELECT add_em(1, 2) AS answer;
answer
--------
3
这是一个更有用的功能,可用于借记银行帐户:
CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
UPDATE bank
SET balance = balance - debit
WHERE accountno = tf1.accountno;
SELECT 1;
$$ LANGUAGE SQL;
用户可以按以下方式执行此功能以从帐户 17 中扣除$ 100.00:
SELECT tf1(17, 100.0);
在此示例中,我们为第一个参数选择了名称accountno
,但这与bank
表中的列的名称相同。在UPDATE
命令中,accountno
引用列bank.accountno
,因此必须使用tf1.accountno
来引用自变量。我们当然可以通过为参数使用其他名称来避免这种情况。
在实践中,人们可能希望函数比常量 1 更有用的结果,因此更可能的定义是:
CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
UPDATE bank
SET balance = balance - debit
WHERE accountno = tf1.accountno;
SELECT balance FROM bank WHERE accountno = tf1.accountno;
$$ LANGUAGE SQL;
调整余额并返回新余额。使用RETURNING
可以在一个命令中完成同一件事:
CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
UPDATE bank
SET balance = balance - debit
WHERE accountno = tf1.accountno
RETURNING balance;
$$ LANGUAGE SQL;
37 .4.3. 复合类型上的 SQL 函数
当使用复合类型的参数编写函数时,我们不仅必须指定所需的参数,还必须指定该参数的所需属性(字段)。例如,假设emp
是一个包含员工数据的表,因此也是该表每一行的复合类型的名称。这是一个函数double_salary
,用于计算某人的薪水加倍后的工资:
CREATE TABLE emp (
name text,
salary numeric,
age integer,
cubicle point
);
INSERT INTO emp VALUES ('Bill', 4200, 45, '(2,1)');
CREATE FUNCTION double_salary(emp) RETURNS numeric AS $$
SELECT $1.salary * 2 AS salary;
$$ LANGUAGE SQL;
SELECT name, double_salary(emp.*) AS dream
FROM emp
WHERE emp.cubicle ~= point '(2,1)';
name | dream
------+-------
Bill | 8400
请注意,使用语法$1.salary
来选择参数行值的一个字段。还请注意,调用SELECT
命令如何使用* table_name
* .*
选择表的整个当前行作为复合值。可以仅使用表名来引用表行,如下所示:
SELECT name, double_salary(emp) AS dream
FROM emp
WHERE emp.cubicle ~= point '(2,1)';
但是不建议使用此方法,因为很容易混淆。 (有关表行的组合值的这两种表示法的详细信息,请参见Section 8.16.5。)
有时,即时构造复合参数值很方便。这可以使用ROW
构造来完成。例如,我们可以调整传递给函数的数据:
SELECT name, double_salary(ROW(name, salary*1.1, age, cubicle)) AS dream
FROM emp;
也可以构建一个返回复合类型的函数。这是一个返回单个emp
行的函数的示例:
CREATE FUNCTION new_emp() RETURNS emp AS $$
SELECT text 'None' AS name,
1000.0 AS salary,
25 AS age,
point '(2,2)' AS cubicle;
$$ LANGUAGE SQL;
在此示例中,我们为每个属性指定了一个常量值,但是任何计算都可以代替这些常量。
请注意有关定义函数的两个重要事项:
查询中的选择列表 Sequences 必须与列在与组合类型相关联的表中出现的 Sequences 完全相同。 (正如我们上面所做的那样,命名列与系统无关.)
您必须对表达式进行类型转换以匹配复合类型的定义,否则会出现如下错误:
ERROR: function declared to return emp returns varchar instead of text at column 1
定义相同功能的另一种方法是:
CREATE FUNCTION new_emp() RETURNS emp AS $$
SELECT ROW('None', 1000.0, 25, '(2,2)')::emp;
$$ LANGUAGE SQL;
在这里,我们编写了一个SELECT
,它只返回正确复合类型的单个列。在这种情况下,这确实不是更好,但是在某些情况下,它是一种方便的选择-例如,如果我们需要通过调用另一个返回所需复合值的函数来计算结果。
我们可以通过在值表达式中使用它来直接调用此函数:
SELECT new_emp();
new_emp
--------------------------
(None,1000.0,25,"(2,2)")
或通过将其作为表函数调用:
SELECT * FROM new_emp();
name | salary | age | cubicle
------+--------+-----+---------
None | 1000.0 | 25 | (2,2)
Section 37.4.7中对第二种方法进行了更全面的描述。
使用返回复合类型的函数时,可能只需要其结果中的一个字段(属性)。您可以使用以下语法来做到这一点:
SELECT (new_emp()).name;
name
------
None
需要额外的括号以防止解析器变得混乱。如果您在没有他们的情况下尝试这样做,则会得到以下信息:
SELECT new_emp().name;
ERROR: syntax error at or near "."
LINE 1: SELECT new_emp().name;
^
另一种选择是使用功能符号来提取属性:
SELECT name(new_emp());
name
------
None
如Section 8.16.5中所述,字段符号和功能符号是等效的。
使用返回复合类型的函数的另一种方法是将结果传递给接受正确行类型作为 Importing 的另一个函数:
CREATE FUNCTION getname(emp) RETURNS text AS $$
SELECT $1.name;
$$ LANGUAGE SQL;
SELECT getname(new_emp());
getname
---------
None
(1 row)
37 .4.4. 带输出参数的 SQL 函数
描述函数结果的另一种方法是使用* output parameters *定义它,如本例所示:
CREATE FUNCTION add_em (IN x int, IN y int, OUT sum int)
AS 'SELECT x + y'
LANGUAGE SQL;
SELECT add_em(3,7);
add_em
--------
10
(1 row)
这与Section 37.4.2中显示的add_em
版本没有本质上的区别。输出参数的 true 价值在于,它们提供了一种方便的方法来定义返回几列的函数。例如,
CREATE FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int)
AS 'SELECT x + y, x * y'
LANGUAGE SQL;
SELECT * FROM sum_n_product(11,42);
sum | product
-----+---------
53 | 462
(1 row)
实际上,这里发生的是我们为函数的结果创建了一个匿名复合类型。上面的示例的最终结果与
CREATE TYPE sum_prod AS (sum int, product int);
CREATE FUNCTION sum_n_product (int, int) RETURNS sum_prod
AS 'SELECT $1 + $2, $1 * $2'
LANGUAGE SQL;
但不必烦恼单独的复合类型定义通常很方便。请注意,附加到输出参数的名称不仅是修饰,还可以确定匿名复合类型的列名称。 (如果省略输出参数的名称,则系统将自行选择一个名称.)
请注意,从 SQL 调用此类函数时,输出参数未包括在调用参数列表中。这是因为 PostgreSQL 仅考虑 Importing 参数来定义函数的调用签名。这也意味着,在出于删除等目的引用函数时,仅 Importing 参数很重要。我们可以使用以下任一方法删除上述功能
DROP FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int);
DROP FUNCTION sum_n_product (int, int);
参数可以标记为IN
(默认值),OUT
,INOUT
或VARIADIC
。 INOUT
参数既用作 Importing 参数(属于调用参数列表的一部分),又用作输出参数(属于结果记录类型的一部分)。 VARIADIC
参数是 Importing 参数,但将按以下说明进行特殊处理。
37 .4.5. 参数数目可变的 SQL 函数
可以声明 SQL 函数接受可变数量的参数,只要所有“可选”参数都具有相同的数据类型即可。可选参数将作为数组传递给函数。通过将最后一个参数标记为VARIADIC
来声明该函数;此参数必须声明为数组类型。例如:
CREATE FUNCTION mleast(VARIADIC arr numeric[]) RETURNS numeric AS $$
SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;
SELECT mleast(10, -1, 5, 4.4);
mleast
--------
-1
(1 row)
实际上,所有在VARIADIC
位置或之后的实际参数都收集到一个一维数组中,就像您已经编写了
SELECT mleast(ARRAY[10, -1, 5, 4.4]); -- doesn't work
但是,您实际上不能编写该代码,或者至少不会与该函数定义匹配。标记为VARIADIC
的参数匹配其元素类型而不是其自身类型的一个或多个出现。
有时,将已经构造的数组传递给可变函数很有用;当一个可变参数函数希望将其数组参数传递给另一个函数时,这特别方便。同样,这是调用模式中发现的可变参数函数的唯一安全方法,该模式允许不受信任的用户创建对象。参见Section 10.3。您可以通过在呼叫中指定VARIADIC
来做到这一点:
SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
这样可以防止将函数的可变参数扩展为其元素类型,从而使数组参数值正常匹配。 VARIADIC
只能附加到函数调用的最后一个实际参数上。
在调用中指定VARIADIC
也是将空数组传递给可变参数函数的唯一方法,例如:
SELECT mleast(VARIADIC ARRAY[]::numeric[]);
简单地写SELECT mleast()
是行不通的,因为可变参数必须至少匹配一个实际参数。 (如果要允许这样的调用,则可以定义第二个也名为mleast
的函数,不带参数)。
从可变参数生成的数组元素参数被视为没有自己的名称。这意味着除非指定了VARIADIC
,否则无法使用命名参数(Section 4.3)调用可变参数函数。例如,这将起作用:
SELECT mleast(VARIADIC arr => ARRAY[10, -1, 5, 4.4]);
但不是这些:
SELECT mleast(arr => 10);
SELECT mleast(arr => ARRAY[10, -1, 5, 4.4]);
37 .4.6. 具有参数默认值的 SQL 函数
可以使用某些或所有 Importing 参数的默认值声明函数。只要调用的函数的实际参数不足,就会插入默认值。由于只能从实际参数列表的末尾省略参数,因此具有默认值的参数之后的所有参数也必须具有默认值。 (尽管使用命名参数表示法可以放宽此限制,但仍会强制执行此设置,以使位置参数表示法合理地起作用.)无论您是否使用它,此功能在某些用户调用数据库中的函数时都需要采取预防措施不信任其他用户;参见Section 10.3。
For example:
CREATE FUNCTION foo(a int, b int DEFAULT 2, c int DEFAULT 3)
RETURNS int
LANGUAGE SQL
AS $$
SELECT $1 + $2 + $3;
$$;
SELECT foo(10, 20, 30);
foo
-----
60
(1 row)
SELECT foo(10, 20);
foo
-----
33
(1 row)
SELECT foo(10);
foo
-----
15
(1 row)
SELECT foo(); -- fails since there is no default for the first argument
ERROR: function foo() does not exist
也可以使用=
符号代替关键字DEFAULT
。
37 .4.7. SQL 函数作为表源
所有 SQL 函数都可以在查询的FROM
子句中使用,但是对于返回复合类型的函数特别有用。如果将函数定义为返回基本类型,则 table 函数将生成一个单列表。如果将函数定义为返回复合类型,则表函数将为复合类型的每个属性生成一列。
这是一个例子:
CREATE TABLE foo (fooid int, foosubid int, fooname text);
INSERT INTO foo VALUES (1, 1, 'Joe');
INSERT INTO foo VALUES (1, 2, 'Ed');
INSERT INTO foo VALUES (2, 1, 'Mary');
CREATE FUNCTION getfoo(int) RETURNS foo AS $$
SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;
SELECT *, upper(fooname) FROM getfoo(1) AS t1;
fooid | foosubid | fooname | upper
-------+----------+---------+-------
1 | 1 | Joe | JOE
(1 row)
如示例所示,我们可以使用函数结果的列,就像它们是常规表的列一样。
请注意,我们仅从函数中获得了一行。这是因为我们没有使用SETOF
。下一节将对此进行描述。
37 .4.8. SQL 函数返回集
当 SQL 函数声明为返回SETOF sometype
时,该函数的最终查询将执行完毕,并且其输出的每一行都将作为结果集的元素返回。
在FROM
子句中调用函数时,通常使用此功能。在这种情况下,函数返回的每一行都变成查询看到的表的一行。例如,假设表foo
具有与上面相同的内容,我们说:
CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$
SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1;
然后我们将得到:
fooid | foosubid | fooname
-------+----------+---------
1 | 1 | Joe
1 | 2 | Ed
(2 rows)
也可以使用输出参数定义的列返回多行,如下所示:
CREATE TABLE tab (y int, z int);
INSERT INTO tab VALUES (1, 2), (3, 4), (5, 6), (7, 8);
CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int)
RETURNS SETOF record
AS $$
SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;
SELECT * FROM sum_n_product_with_tab(10);
sum | product
-----+---------
11 | 10
13 | 30
15 | 50
17 | 70
(4 rows)
这里的关键点是您必须写RETURNS SETOF record
来指示该函数返回多行而不是仅一行。如果只有一个输出参数,请写出该参数的类型而不是record
。
通过多次调用 set-returning 函数构造查询结果通常很有用,每次调用的参数都来自表或子查询的连续行。首选的方法是使用Section 7.2.1.5中描述的LATERAL
关键字。这是一个使用 set-returning 函数枚举树结构元素的示例:
SELECT * FROM nodes;
name | parent
-----------+--------
Top |
Child1 | Top
Child2 | Top
Child3 | Top
SubChild1 | Child1
SubChild2 | Child1
(6 rows)
CREATE FUNCTION listchildren(text) RETURNS SETOF text AS $$
SELECT name FROM nodes WHERE parent = $1
$$ LANGUAGE SQL STABLE;
SELECT * FROM listchildren('Top');
listchildren
--------------
Child1
Child2
Child3
(3 rows)
SELECT name, child FROM nodes, LATERAL listchildren(name) AS child;
name | child
--------+-----------
Top | Child1
Top | Child2
Top | Child3
Child1 | SubChild1
Child1 | SubChild2
(5 rows)
这个例子并没有做我们用简单的连接所无法完成的任何事情,但是在更复杂的计算中,将某些工作放到一个函数中的选择可能非常方便。
返回集的函数也可以在查询的选择列表中调用。对于查询本身生成的每一行,都将调用 set-returning 函数,并为函数结果集中的每个元素生成一个输出行。前面的示例也可以通过以下查询完成:
SELECT listchildren('Top');
listchildren
--------------
Child1
Child2
Child3
(3 rows)
SELECT name, listchildren(name) FROM nodes;
name | listchildren
--------+--------------
Top | Child1
Top | Child2
Top | Child3
Child1 | SubChild1
Child1 | SubChild2
(5 rows)
请注意,在最后一个SELECT
中,没有出现Child2
,Child3
等的输出行。这是因为listchildren
返回了这些参数的空集,所以没有生成结果行。这与使用LATERAL
语法时从内部联接到函数结果的行为相同。
PostgreSQL 对查询的选择列表中的返回集合函数的行为几乎完全等同于将返回集合函数写在LATERAL FROM
子句中。例如,
SELECT x, generate_series(1,5) AS g FROM tab;
几乎等于
SELECT x, g FROM tab, LATERAL generate_series(1,5) AS g;
完全相同,只是在此特定示例中,计划者可以选择将g
放在 nestloop 联接的外部,因为g
对tab
没有实际的横向依赖性。这将导致不同的输出行 Sequences。选择列表中的返回集合的函数始终会像位于嵌套循环内部一样与FROM
子句的其余部分一起进行求值,以便在FROM
子句的下一行成为考虑过的。
如果查询的选择列表中有多个返回集合的函数,则其行为类似于将函数放入单个LATERAL ROWS FROM( ... )
FROM
子句中所得到的结果。对于基础查询的每一行,都有一个使用每个函数的第一个结果的输出行,然后是使用第二个结果的输出行,依此类推。如果某些集合返回函数产生的输出少于其他函数,则将空值替换为丢失的数据,以便为一个基础行发出的总行数与产生最多输出的集合返回函数的行数相同。因此,返回设置的函数“按步进行”,直到它们全部用尽,然后从下一个下一行 continue 执行。
返回设置的函数可以嵌套在选择列表中,尽管FROM
子句项中不允许这样做。在这种情况下,嵌套的每个级别都被单独对待,就好像它是单独的LATERAL ROWS FROM( ... )
项一样。例如,在
SELECT srf1(srf2(x), srf3(y)), srf4(srf5(z)) FROM tab;
对于tab
的每一行,将以锁步方式运行返回集合的函数srf2
,srf3
和srf5
,然后将srf1
和srf4
以锁步方式应用于由较低函数产生的每一行。
返回集函数不能在条件评估构造中使用,例如CASE
或COALESCE
。例如,考虑
SELECT x, CASE WHEN x > 0 THEN generate_series(1, 5) ELSE 0 END FROM tab;
看起来这应该产生五个重复的具有x > 0
的 Importing 行,以及一个不具有_的 Importing 行的重复。但实际上,因为在评估CASE
表达式之前,将在隐式LATERAL FROM
项中运行generate_series(1, 5)
,所以它将对每个 Importing 行产生 5 次重复。为了减少混乱,此类情况会产生解析时错误。
Note
如果函数的最后一个命令是INSERT
,UPDATE
或DELETE
和RETURNING
,则即使该函数未使用SETOF
声明或调用查询未获取所有结果行,该命令也将始终执行完毕。 RETURNING
子句产生的任何多余的行都将被静默删除,但是命令表修改仍会发生(并且在从函数返回之前全部完成)。
Note
在 PostgreSQL 10 之前,除非将总是返回相同数量的行,否则在同一个选择列表中放置多个返回集合的函数的行为就不是很明智。否则,您得到的输出行数等于 set-returning 函数产生的行数的最小公倍数。同样,嵌套的集合返回函数不能如上所述工作。相反,一个返回集合的函数最多可以有一个返回集合的参数,并且每个返回集合的嵌套都独立运行。另外,以前允许条件执行(CASE
等内部的集合返回函数),这使事情更加复杂。在编写需要在旧 PostgreSQL 版本中运行的查询时,建议使用LATERAL
语法,因为这将在不同版本之间提供一致的结果。如果查询依赖于有条件执行集合返回函数,则可以通过将条件测试移到自定义的有集合返回函数中来对其进行修复。例如,
SELECT x, CASE WHEN y > 0 THEN generate_series(1, z) ELSE 5 END FROM tab;
could become
CREATE FUNCTION case_generate_series(cond bool, start int, fin int, els int)
RETURNS SETOF int AS $$
BEGIN
IF cond THEN
RETURN QUERY SELECT generate_series(start, fin);
ELSE
RETURN QUERY SELECT els;
END IF;
END$$ LANGUAGE plpgsql;
SELECT x, case_generate_series(y > 0, 1, z, 5) FROM tab;
此公式在所有版本的 PostgreSQL 中都将相同。
37 .4.9. SQL 函数返回表
还有一种将函数声明为返回集合的方法,即使用语法RETURNS TABLE(columns)
。这等效于使用一个或多个OUT
参数,并将函数标记为返回SETOF record
(或SETOF
单个输出参数的类型,视情况而定)。该符号在 SQL 标准的最新版本中指定,因此比使用SETOF
更可移植。
例如,前面的“求和积”示例也可以通过以下方式完成:
CREATE FUNCTION sum_n_product_with_tab (x int)
RETURNS TABLE(sum int, product int) AS $$
SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;
不允许使用带有RETURNS TABLE
表示法的显式OUT
或INOUT
参数-您必须将所有输出列都放在TABLE
列表中。
37 .4.10. 多态 SQL 函数
可以声明 SQL 函数以接受并返回多态类型anyelement
,anyarray
,anynonarray
,anyenum
和anyrange
。有关多态函数的更详细说明,请参见Section 37.2.5。这是一个多态函数make_array
,它从两个任意数据类型元素构建一个数组:
CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS $$
SELECT ARRAY[$1, $2];
$$ LANGUAGE SQL;
SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;
intarray | textarray
----------+-----------
{1,2} | {a,b}
(1 row)
注意,使用类型转换'a'::text
来指定参数的类型为text
。如果参数只是字符串 Literals,则必须这样做,因为否则它将被视为类型unknown
,并且unknown
的数组不是有效类型。没有类型转换,您将得到如下错误:
ERROR: could not determine polymorphic type because input has type "unknown"
允许具有固定返回类型的多态参数,但反之则不行。例如:
CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS boolean AS $$
SELECT $1 > $2;
$$ LANGUAGE SQL;
SELECT is_greater(1, 2);
is_greater
------------
f
(1 row)
CREATE FUNCTION invalid_func() RETURNS anyelement AS $$
SELECT 1;
$$ LANGUAGE SQL;
ERROR: cannot determine result data type
DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
多态可以与具有输出参数的函数一起使用。例如:
CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE SQL;
SELECT * FROM dup(22);
f2 | f3
----+---------
22 | {22,22}
(1 row)
多态也可以与可变函数一起使用。例如:
CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;
SELECT anyleast(10, -1, 5, 4);
anyleast
----------
-1
(1 row)
SELECT anyleast('abc'::text, 'def');
anyleast
----------
abc
(1 row)
CREATE FUNCTION concat_values(text, VARIADIC anyarray) RETURNS text AS $$
SELECT array_to_string($2, $1);
$$ LANGUAGE SQL;
SELECT concat_values('|', 1, 4, 2);
concat_values
---------------
1|4|2
(1 row)
37 .4.11. 带排序规则的 SQL 函数
当 SQL 函数具有一个或多个可排序数据类型的参数时,将根据分配给实际参数的排序规则为每个函数调用标识一个排序规则,如Section 23.2中所述。如果成功识别了排序规则(即,参数之间没有隐式排序规则的冲突),则所有可排序参数都被视为隐式具有该排序规则。这将影响函数中对排序规则敏感的操作的行为。例如,使用上述anyleast
函数,
SELECT anyleast('abc'::text, 'ABC');
将取决于数据库的默认排序规则。在C
语言环境中,结果将是ABC
,但在其他许多语言环境中,结果将是abc
。可以通过在所有参数中添加COLLATE
子句来强制使用排序规则
SELECT anyleast('abc'::text, 'ABC' COLLATE "C");
另外,如果您希望函数使用特定的排序规则进行操作而不管其调用什么内容,请在函数定义中根据需要插入COLLATE
子句。此版本的anyleast
将始终使用en_US
语言环境来比较字符串:
CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
SELECT min($1[i] COLLATE "en_US") FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;
但是请注意,如果将其应用于不可整理的数据类型,则将引发错误。
如果无法在实际参数之间识别出通用的排序规则,则 SQL 函数会将其参数视为具有其数据类型的默认排序规则(通常是数据库的默认排序规则,但对于域类型的参数可能会有所不同)。
可将可排序参数的行为视为多态性的一种受限形式,仅适用于文本数据类型。