pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
目录
事实证明,定义新类型的一部分工作就是定义描述其行为的函数。因此,虽然可以不定义新类型就定义新函数,反过来却不成立。所以我们在描述如何向 Postgres 添加新类型之前,先描述如何添加新函数。
Postgres SQL 提供三种类型的函数:
查询语言函数(用 SQL 编写的函数)
过程语言函数(例如用 PLTCL 或 PLSQL 编写的函数)
编程语言函数(用 C 这样的编译型编程语言编写的函数)
每种函数都可以接受基本类型、复合类型或它们的某种组合作为参数。此外,每种函数都可以返回基本类型或复合类型。定义 SQL 函数最容易,所以我们从它开始。本节的示例也可以在 funcs.sql 和 funcs.c 中找到。
SQL 函数执行一个任意的 SQL 查询列表,返回列表中最后一个查询的结果。SQL 函数一般返回集合。如果其返回类型没有指定为 setof,则返回最后一个查询结果中的一个任意元素。
AS 之后的 SQL 函数体应是用分号分隔并用单引号括起的查询列表。注意,查询中使用的引号必须转义,即在前面加一个反斜杠。
SQL 函数的参数可以在查询中用 $n 语法引用:$1 指第一个参数,$2 指第二个参数,依此类推。如果参数是复合类型,则可以使用点记法(例如 "$1.emp") 访问参数的属性或调用函数。
为说明一个简单的 SQL 函数,考虑下面这个可以用来给银行账户扣款的例子:
CREATE FUNCTION tp1 (int4, float8)
RETURNS int4
AS 'UPDATE bank
SET balance = bank.balance - $2
WHERE bank.acctountno = $1;
SELECT 1;'
LANGUAGE 'sql';
用户可以执行如下命令给账户 17 扣款 $100.00:
SELECT tp1( 17,100.0);
下面的示例更有趣,它接受一个 EMP 类型的参数并检索多个结果:
CREATE FUNCTION hobbies (EMP) RETURNS SETOF hobbies
AS 'SELECT hobbies.* FROM hobbies
WHERE $1.name = hobbies.person'
LANGUAGE 'sql';
最简单的 SQL 函数没有参数,只是返回一个基本类型,例如 int4:
CREATE FUNCTION one()
RETURNS int4
AS 'SELECT 1 as RESULT;'
LANGUAGE 'sql';
SELECT one() AS answer;
+-------+
|answer |
+-------+
|1 |
+-------+
注意我们为函数结果定义了一个列名(名为 RESULT),但这个列名在函数之外不可见。因此结果被标记为 answer 而不是 one。
定义以基本类型为参数的 SQL 函数几乎同样容易。在下面的示例中,注意我们如何在函数内把参数引用为 $1 和 $2:
CREATE FUNCTION add_em(int4, int4)
RETURNS int4
AS 'SELECT $1 + $2;'
LANGUAGE 'sql';
SELECT add_em(1, 2) AS answer;
+-------+
|answer |
+-------+
|3 |
+-------+
指定带复合类型(如 EMP)参数的函数时,我们不仅要指明想要哪个参数(如上面用 $1 和 $2),还要指明该参数的属性。例如,取函数 double_salary,它计算你的薪资翻倍后的值:
CREATE FUNCTION double_salary(EMP)
RETURNS int4
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 |
+-----+-------+
|Sam | 2400 |
+-----+-------+
注意语法 $1.salary 的使用。在进入返回复合类型的函数这一主题之前,我们必须先介绍投影属性的函数记法。简单的解释是:attribute(table) 和 table.attribute 这两种记法通常可以互换使用:
--
-- this is the same as:
-- SELECT EMP.name AS youngster FROM EMP WHERE EMP.age < 30
--
SELECT name(EMP) AS youngster
FROM EMP
WHERE age(EMP) < 30;
+----------+
|youngster |
+----------+
|Sam |
+----------+
然而如下文所见,情况并不总是如此。当我们想使用返回单行的函数时,这种函数记法很重要。我们通过在函数内逐属性地组装整个行来实现。这是一个返回单个 EMP 行的函数示例:
CREATE FUNCTION new_emp()
RETURNS EMP
AS 'SELECT text ''None'' AS name,
1000 AS salary,
25 AS age,
point ''(2,2)'' AS cubicle'
LANGUAGE 'sql';
在本例中,我们为每个属性都指定了常量值,但这些常量可以换成任何计算或表达式。定义这样的函数可能有些棘手。较重要的一些注意事项如下:
查询中的目标列表顺序必须与定义该复合类型的 CREATE TABLE 语句中属性出现的顺序完全相同。
你必须对表达式进行类型转换,使之与复合类型的定义匹配,否则会得到这样的错误:
ERROR: function declared to return emp returns varchar instead of text at column 1
调用返回行的函数时,我们无法检索整个行。我们必须要么从行中投影出一个属性,要么把整行传递给另一个函数。
SELECT name(new_emp()) AS nobody;
+-------+
|nobody |
+-------+
|None |
+-------+
一般而言,我们必须使用函数语法投影函数返回值的属性,原因就是解析器还不理解另一种(点)语法与函数调用组合使用做投影。
SELECT new_emp().name AS nobody;
NOTICE:parser: syntax error at or near "."
SQL 查询语言中任何命令的集合都可以打包到一起并定义为函数。这些命令既可以是更新(即 INSERT、UPDATE 和 DELETE),也可以是 SELECT 查询。但最后一条命令必须是一个 SELECT,它返回函数返回类型所指定的内容。
CREATE FUNCTION clean_EMP ()
RETURNS int4
AS 'DELETE FROM EMP
WHERE EMP.salary <= 0;
SELECT 1 AS ignore_this;'
LANGUAGE 'sql';
SELECT clean_EMP();
+--+
|x |
+--+
|1 |
+--+
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。