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

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 / 7.1 / 7.0 / 6.5 / 6.4
历史版本PostgreSQL 7.1 已于 2006 年 4 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

第 13 章 扩展 SQL:函数

事实证明,定义新类型的一部分工作就是定义描述其行为的函数。因此,虽然可以不定义新类型就定义新函数,反过来却不成立。所以我们在描述如何向 Postgres 添加新类型之前,先描述如何添加新函数。

Postgres SQL 提供三种类型的函数:

  • 查询语言函数(用 SQL 编写的函数)

  • 过程语言函数(例如用 PLTCL 或 PLSQL 编写的函数)

  • 编程语言函数(用 C 这样的编译型编程语言编写的函数)

每种函数都可以接受基本类型、复合类型或它们的某种组合作为参数。此外,每种函数都可以返回基本类型或复合类型。定义 SQL 函数最容易,所以我们从它开始。本节的示例也可以在 funcs.sql 和 funcs.c 中找到。

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

SQL 函数执行一个任意的 SQL 查询列表,返回列表中最后一个查询的结果。SQL 函数一般返回集合。如果其返回类型没有指定为 setof,则返回最后一个查询结果中的一个任意元素。

AS 之后的 SQL 函数体应是用分号分隔并用单引号括起的查询列表。注意,查询中使用的引号必须转义,即在前面加一个反斜杠。

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

13.1.1. 示例

为说明一个简单的 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';
     

13.1.2. 基本类型上的 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      |
+-------+
     

13.1.3. 复合类型上的 SQL 函数

指定带复合类型(如 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 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。