↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2
历史版本PostgreSQL 7.3 已于 2007 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

9.2. 查询语言(SQL)函数 #

SQL 函数执行一个任意的 SQL 语句列表,返回列表中最后一个查询的结果,该查询必须是一个 SELECT。在简单(非集合)的情况下,将返回最后一个查询结果的第一行。(请记住,除非使用 ORDER BY,多行结果的“第一行”并没有良好定义。)如果最后一个查询碰巧不返回任何行,则返回 NULL。

或者,也可以把 SQL 函数声明为返回集合,方法是把函数的返回类型指定为 SETOF sometype。此时将返回最后一个查询结果的所有行。更多细节见下文。

SQL 函数体应是一个或多个用分号分隔的 SQL 语句的列表。注意,由于 CREATE FUNCTION 命令的语法要求函数体用单引号括起,因此函数体中使用的单引号(')必须转义:在需要引号的地方写两个单引号('')或一个反斜杠(\')。

SQL 函数的参数可以在函数体中使用语法 $n 引用:$1 指第一个参数,$2 指第二个参数,依此类推。如果参数是复合类型,则可以使用“点记法”(例如 $1.emp)访问该参数的属性。

9.2.1. 示例

为说明一个简单的 SQL 函数,考虑下面这个可以用来给银行账户扣款的例子:

CREATE FUNCTION tp1 (integer, numeric) RETURNS integer AS '
    UPDATE bank 
        SET balance = balance - $2
        WHERE accountno = $1;
    SELECT 1;
' LANGUAGE SQL;

用户可以执行如下命令给账户 17 扣款 $100.00:

SELECT tp1(17, 100.0);

实践中人们可能希望函数返回比常量 “1” 更有用的结果,所以更可能的定义是

CREATE FUNCTION tp1 (integer, numeric) RETURNS numeric AS '
    UPDATE bank 
        SET balance = balance - $2
        WHERE accountno = $1;
    SELECT balance FROM bank WHERE accountno = $1;
' LANGUAGE SQL;

它调整余额并返回新余额。

SQL 语言中任何命令的集合都可以打包到一起并定义为函数。这些命令既可以是数据修改(即 INSERT、UPDATE 和 DELETE),也可以是 SELECT 查询。但最后一条命令必须是一个 SELECT,它返回函数返回类型所指定的内容。或者,如果你想定义一个执行操作但没有有用值可返回的 SQL 函数,可以把它定义为返回 void。此时函数不能以 SELECT 结尾。例如:

CREATE FUNCTION clean_EMP () RETURNS void AS '
    DELETE FROM EMP 
        WHERE EMP.salary <= 0;
' LANGUAGE SQL;

SELECT clean_EMP();
 clean_emp
-----------

(1 row)

9.2.2. 基本类型上的 SQL 函数

最简单的 SQL 函数没有参数,只是返回一个基本类型,例如 integer:

CREATE FUNCTION one() RETURNS integer AS '
    SELECT 1 as RESULT;
' LANGUAGE SQL;

SELECT one();
 one
-----
   1

注意我们在函数体内为函数结果定义了一个列别名(名为 RESULT),但这个列别名在函数之外不可见。因此结果被标记为 one 而不是 RESULT。

定义以基本类型为参数的 SQL 函数几乎同样容易。在下面的示例中,注意我们如何在函数内把参数引用为 $1 和 $2:

CREATE FUNCTION add_em(integer, integer) RETURNS integer AS '
    SELECT $1 + $2;
' LANGUAGE SQL;

SELECT add_em(1, 2) AS answer;
 answer
--------
      3

9.2.3. 复合类型上的 SQL 函数

指定带复合类型参数的函数时,我们不仅要指明想要哪个参数(如上面用 $1 和 $2),还要指明该参数的属性。例如,设 EMP 是一个包含员工数据的表,因而也是该表每行的复合类型的名称。下面的函数 double_salary 计算你的薪资翻倍后的值:

CREATE FUNCTION double_salary(EMP) RETURNS integer 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 选择参数行值的一个字段。还要注意调用它的 SELECT 命令如何用表名把该表的整个当前行表示为一个复合值。

也可以构建返回复合类型的函数。 下面是一个返回单个 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;

在本例中,我们为每个属性都指定了常量值,但这些常量可以换成任何计算或表达式。定义该函数时有两点重要注意事项:

  • 查询中的目标列表顺序必须与列在该复合类型所关联的表中出现的顺序完全相同。(像上面这样为列命名,系统并不理会。)

  • 你必须对表达式进行类型转换,使之与复合类型的定义匹配,否则会得到这样的错误:

    ERROR:  function declared to return emp returns varchar instead of text at column 1
    

返回行(复合类型)的函数可以用作表函数,如下文所述。它也可以在 SQL 表达式的上下文中调用,但仅当从行中提取单个属性,或把整行传递给另一个接受相同复合类型的函数时。例如,

SELECT (new_emp()).name;
 name
------
 None

我们需要额外的圆括号来避免解析器混淆:

SELECT new_emp().name;
ERROR:  parser: parse error at or near "."

另一种选择是使用函数记法提取属性。简单的解释是:attribute(table) 和 table.attribute 这两种记法可以互换使用:

SELECT name(new_emp());
 name
------
 None
--
-- 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

使用返回行的函数结果的另一种方式是声明一个接受行类型参数的第二个函数,并把函数的结果传给它:

CREATE FUNCTION getname(emp) RETURNS text AS
'SELECT $1.name;'
LANGUAGE SQL;
SELECT getname(new_emp());
 getname
---------
 None
(1 row)

9.2.4. SQL 表函数

表函数是可以在查询的 FROM 子句中使用的函数。所有 SQL 语言函数都可以以这种方式使用,但对返回复合类型的函数尤其有用。如果函数定义为返回基本类型,则表函数产生一个单列表。如果函数定义为返回复合类型,则表函数为复合类型的每一列产生一列。

下面是一个示例:

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
(2 rows)

如示例所示,我们可以像使用普通表的列一样使用函数结果的各列。

注意我们只从函数得到了一行。这是因为我们没有使用 SETOF。

9.2.5. 返回集合的 SQL 函数

当一个 SQL 函数被声明为返回 SETOF sometype 时,该函数的最后一个 SELECT 查询会被执行完,并且它输出的每一行都会作为结果集的一个元素返回。

此特性通常用于把函数作为表函数调用。此时函数返回的每一行都成为查询所见表的一行。例如,设表 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)

目前,返回集合的函数也可以在 SELECT 查询的目标列表中调用。对 SELECT 自身生成的每一行,都会调用一次返回集合的函数,并为函数结果集的每个元素生成一个输出行。但要注意,此能力已被弃用,在未来的版本中可能被移除。下面是一个在目标列表中返回集合的函数示例:

CREATE FUNCTION listchildren(text) RETURNS SETOF text AS
'SELECT name FROM nodes WHERE parent = $1'
LANGUAGE SQL;
SELECT * FROM nodes;
   name    | parent
-----------+--------
 Top       |
 Child1    | Top
 Child2    | Top
 Child3    | Top
 SubChild1 | Child1
 SubChild2 | Child1
(6 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 对这些输入返回空集,因此不会生成输出行。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。