pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
SQL 函数执行由任意 SQL 语句组成的一个列表,并返回列表中最后一个查询的结果。在简单(非集合)情况下,将返回最后一个查询结果的第一行。(请记住,除非使用 ORDER BY,否则多行结果中的“第一行”并没有明确定义。)如果最后一个查询完全没有返回任何行,则返回空值。
或者,一个 SQL 函数可以通过指定函数的返回类型为SETOF 被声明为返回一个集合(也就是多个行),或者等效地声明它为sometypeRETURNS TABLE(。在这种情况下,最后一个查询的结果的所有行会被返回。下文将给出进一步的细节。columns)
SQL 函数的函数体必须是一个由分号分隔的 SQL 语句列表。最后一条语句后的分号是可选的。除非该函数被声明为返回 void,否则最后一条语句必须是 SELECT,或者是 INSERT、UPDATE 或 DELETE 并带有 RETURNING 子句。
任意一组 SQL 语言命令都可以打包并定义成函数。除 SELECT 查询外,这些命令还可以包含数据修改查询(INSERT, UPDATE、DELETE),以及其他 SQL 命令。(不能使用事务控制命令,例如 COMMIT, SAVEPOINT,以及某些工具命令,例如 VACUUM,来编写 SQL 函数。)不过,最后一条命令必须是 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)
SQL 函数的整个函数体都会先经过解析,然后才开始执行。虽然 SQL 函数可以包含修改系统目录的命令(例如 CREATE TABLE),但在对函数中的后续命令进行解析分析时,这些命令的效果还不可见。因此,例如将 CREATE TABLE foo (...); INSERT INTO foo VALUES(...); 打包到同一个 SQL 函数中,就不会按预期工作,因为 foo 在解析 INSERT 命令时还不存在。在这类情况下,建议使用 PL/pgSQL 而不是 SQL 函数。
CREATE FUNCTION 命令的语法要求把函数体写成一个字符串常量。对于字符串常量,通常使用美元引用最方便(见第 4.1.2.4 节)。如果你选择使用常规的单引号字符串常量语法,那么必须在函数体中将单引号(')和反斜线(\)写成双份(假定使用转义字符串语法,见第 4.1.2.1 节)。
一个 SQL 函数的参数可以在函数体中用名称或编号引用。下面会有两种方法的示例。
要使用一个名称,将函数参数声明为带有一个名称,然后在函数体中只写该名称。如果参数名称与函数内当前 SQL 命令中的任意列名相同,列名将优先。如果不想这样,可以用函数本身的名称来限定参数名,也就是(如果这会与一个被限定的列名冲突,照例还是列名赢得优先。你可以通过为 SQL 命令中的表选择一个不同的别名来避免这种混淆)。function_name.argument_name
在更旧的数字方法中,参数可以用语法$引用:n$1指的是第一个输入参数,$2指的是第二个,以此类推。不管特定的参数是否使用名称声明,这种方法都有效。
如果一个参数是一种复合类型,那么点号记法(如 或argname.fieldname$1.)也可以被用来 访问该参数的属性。同样,你可能需要用函数的名称来限定参数的名称以避免歧义。fieldname
SQL 函数参数只能被用做数据值而不能作为标识符。例如这是合理的:
INSERT INTO mytable VALUES ($1);
但这样就不行:
INSERT INTO $1 VALUES (42);
使用名称来引用 SQL 函数参数的能力是在PostgreSQL 9.2 中加入的。要在老的服务器中使用的函数必须使用$记法。n
最简单的 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 integer 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 integer 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 integer AS $$
UPDATE bank
SET balance = balance - debit
WHERE accountno = tf1.accountno
RETURNING balance;
$$ LANGUAGE 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)';
但这种用法已被弃用,因为它很容易让人混淆(关于表行复合值这两种记法的更多细节,见第 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;
在这个示例中,我们为每一个属性指定了一个常量值,但是可以用任何计算来替换这些常量。
定义该函数时有两点重要注意事项:
查询中的选择列表顺序必须与列在该复合类型所关联的表中出现的顺序完全相同。(系统不考虑像上面这样指定的列名。)
你必须对表达式进行类型转换,使之与复合类型的定义匹配,否则会得到这样的错误:
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)
第二种方式会在第 36.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
如第 8.16.5 节所述,字段记法和函数记法是等价的。
另一种使用函数返回复合类型的方法是将结果传递给另一个接受正确行类型作为输入的函数:
CREATE FUNCTION getname(emp) RETURNS text AS $$
SELECT $1.name;
$$ LANGUAGE SQL;
SELECT getname(new_emp());
getname
---------
None
(1 row)
描述函数结果的另一种方法是使用输出参数,例如:
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)
这与第 36.4.2 节中展示的 add_em 版本本质上并无不同。输出参数的真正价值在于, 它们为定义返回多列的函数提供了一种方便的方法。例如:
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只考虑输入参数来定义函数的调用签名。这也意味着在为诸如删除函数等目的引用该函数时只有输入参数有关系。我们可以用下面的命令之一删除上述函数
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参数既作为一个输入参数(调用参数列表的一部分)又作为一个输出参数(结果记录类型的一部分)。 VARIADIC参数是输入参数,但被按照下文所述特殊对待。
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 的参数匹配的是其元素类型出现一次或多次, 而不是它自身的数组类型。
有时候,能够把一个已经构造好的数组传给可变参数函数会很有用,尤其是当一个可变参数函数想把它的数组参数再传给另一个函数时。此外,在允许不受信任用户创建对象的模式中调用可变参数函数时,这是唯一安全的方式,见第 10.3 节。你可以在调用中指定VARIADIC来做到这一点:
SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
这样会阻止函数的可变参数按其元素类型展开,从而让数组实参能够按常规方式匹配。VARIADIC 只能附加在函数调用的最后一个实参上。
在调用中指定VARIADIC也是向可变参数函数传递空数组的唯一方式,例如:
SELECT mleast(VARIADIC ARRAY[]::numeric[]);
仅仅写成SELECT mleast()是行不通的,因为可变参数必须匹配至少一个实参。(如果你希望允许这种调用,可以再定义一个同名且不带参数的函数mleast。)
从可变参数派生出的数组元素参数会被视为没有自己的名字。这意味着除非你指定了 VARIADIC,否则不能使用命名参数来调用可变参数函数(第 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]);
函数可以被声明为对一些或者所有输入参数具有默认值。只要调用函数时 没有给出足够多的实参,就会插入默认值来弥补缺失的实参。由于参数只 能从实参列表的尾部开始被省略,在一个有默认值的参数之后的所有参数 都不得不也具有默认值(尽管使用命名参数记法可以允许放松这种限制, 这种限制仍然会被强制以便位置参数记法能工作)。不管你是否使用它,这种能力都要求在某些用户不信任其他用户的数据库中调用函数时做一些预防措施,见第 10.3 节。
例如:
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。
所有的 SQL 函数都可以被用在查询的FROM子句中,但是 对于返回复合类型的函数特别有用。如果函数被定义为返回一种基础类型, 该表函数会产生一个单列表。如果该函数被定义为返回一种复合类型,该 表函数会为该复合类型的每一个属性产生一列。
以下是一个示例:
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。 这会在下一节中介绍。
当一个 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。
通过多次调用集合返回函数来构造查询结果通常很有用,其中每次调用的参数都来自表或子查询的连续行。首选方式是使用 LATERAL 关键字,详见第 7.2.1.5 节。下面是一个使用集合返回函数枚举树结构元素的示例:
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)
这个例子并没有做出任何无法用简单连接实现的事,但在更复杂的计算中,把一部分工作放进函数里通常会非常方便。
集合返回函数也可以出现在查询的选择列表中。对于查询本身生成的每一行,集合返回函数都会被调用,并为其结果集中的每一个元素生成一行输出。前面的例子也可以写成这样:
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 语法把函数结果做内连接时得到的行为相同。
如果一个函数的最后一条命令是 INSERT、UPDATE 或 DELETE 并带有 RETURNING,那么即使该函数没有声明为 SETOF,或者调用它的查询并未取走全部结果行,该命令也总会执行到完成。RETURNING 子句产生的额外结果行会被静默丢弃,但相应的表修改仍然会发生,并且会在函数返回前全部完成。
在选择列表中而不是 FROM 子句中使用集合返回函数的关键问题在于, 把多个集合返回函数放在同一个选择列表中时,行为并不十分合理。 (如果这样做,实际得到的输出行数是每个集合返回函数各自产生行数的最小公倍数。) 在调用多个集合返回函数时,LATERAL 语法产生的结果更不容易令人意外,通常应改用它。
TABLE的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;
不允许把显式的OUT或者INOUT参数用于 RETURNS TABLE记法 — 必须把所有输出列放在 TABLE列表中。
SQL 函数可以被声明为接受并返回这些多态类型:anyelement, anyarray, anynonarray, anyenum、anyrange。关于多态函数的更详细解释,见 第 36.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。如果该参数只是一个字符串字面量,就必须这样做,否则它会被当作 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)
当一个 SQL 函数有一个或多个支持排序规则的数据类型参数时,会根据实参所带的排序规则,为每次函数调用确定一个排序规则,详见 第 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 函数会把参数视为带有其数据类型的默认排序规则 (通常是数据库的默认排序规则,但对域类型参数来说也可能不同)。
这种支持排序规则的参数的行为,可以看作是仅适用于文本数据类型的一种受限多态形式。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。