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

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.0 已于 2005 年 5 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

第 38 章 扩展 SQL:函数

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

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

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

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

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

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

38.1. 查询语言(SQL)函数

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

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

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

38.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 (x = TP1( 17,100.0));
     

下面的示例更有趣,它接受一个 EMP 类型的参数并检索多个结果:

select function hobbies (EMP) returns set of HOBBIES
    as 'select HOBBIES.* from HOBBIES
        where $1.name = HOBBIES.person'
    language 'sql';
     

38.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      |
     +-------+
     

38.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 ~= '(2,1)'::point;
 

     +-----+-------+
     |name | dream |
     +-----+-------+
     |Sam  | 2400  |
     +-----+-------+
     

注意语法 $1.salary 的使用。在进入返回复合类型的函数这一主题之前,我们必须先介绍投影属性的函数记法。简单的解释是:attribute(class) 和 class.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 \'None\'::text AS name,
                1000 AS salary,
                25 AS age,
                \'(2,2)\'::point AS cubicle'
LANGUAGE 'sql';
     

在本例中,我们为每个属性都指定了常量值,但这些常量可以换成任何计算或表达式。定义这样的函数可能有些棘手。较重要的一些注意事项如下:

  • 查询中的目标列表顺序必须与定义该复合类型的 CREATE TABLE 语句(或执行 .* 查询时)中属性出现的顺序完全相同。

  • 你必须(使用 ::)非常小心地对表达式进行类型转换,否则会看到如下错误:

         NOTICE::function declared to return type EMP does not retrieve (EMP.*)
             
            
    
  • 调用返回实例的函数时,我们无法检索整个实例。我们必须要么从实例中投影出一个属性,要么把整个实例传递给另一个函数。

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