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

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

19.6. 控制结构 #

控制结构也许是 PL/pgSQL 最有用(也最重要)的部分。使用 PL/pgSQL 的控制结构,你可以非常灵活而强大地操纵 PostgreSQL 数据。

19.6.1. 从函数返回 #

RETURN expression;

带表达式的 RETURN 用于从不返回集合的 PL/pgSQL 函数返回。函数终止, expression 的值被返回给调用者。

要返回复合(行)值,必须把一个记录或行变量写作 expression。返回标量类型时,可以使用任何 表达式。表达式的结果会像赋值所描述的那样自动转换为函数的返回类型。 (如果你已把函数声明为返回 void,则表达式可以省略, 并且在任何情况下都会被忽略。)

函数的返回值不能没有定义。如果控制到达函数顶层块的末尾而没有遇到 RETURN 语句,将发生运行时错误。

当 PL/pgSQL 函数被声明为返回 SETOF sometype 时,要遵循的过程 略有不同。这种情况下,要返回的各个项在 RETURN NEXT 命令中指定, 然后用一条不带参数的最终 RETURN 命令表示函数已执行完毕。 RETURN NEXT 既可用于标量数据类型,也可用于复合数据类型;后者将返回 整张"表"的结果。使用 RETURN NEXT 的函数应按以下方式调用:

SELECT * FROM some_func();

也就是说,函数在 FROM 子句中用作表源。

RETURN NEXT expression;

RETURN NEXT 实际上并不从函数返回;它只是保存表达式(或记录或行变量, 视返回的数据类型而定)的值。然后执行继续到 PL/pgSQL 函数中的下一条语句。随着相继的 RETURN NEXT 命令被执行,结果集逐渐建立。最后的 RETURN(不需要带 参数)使控制退出函数。

注意

PL/pgSQL 当前对 RETURN NEXT 的实现是在 从函数返回之前保存整个结果集,如上所述。这意味着,如果 PL/pgSQL 函数产生非常大的结果集,性能 可能不佳:为避免耗尽内存,数据会被写入磁盘,但函数本身要等到整个 结果集生成之后才会返回。PL/pgSQL 的未来 版本可能允许用户定义没有这一限制的返回集合的函数。目前,数据开始 写入磁盘的时机由 SORT_MEM 配置变量控制。有足够内存把 更大结果集保存在内存中的管理员应考虑增大此参数。

19.6.2. 条件 #

IF 语句让你根据某些条件执行命令。 PL/pgSQL 有四种 IF 形式:

  • IF ... THEN

  • IF ... THEN ... ELSE

  • IF ... THEN ... ELSE IF 和

  • IF ... THEN ... ELSIF ... THEN ... ELSE

19.6.2.1. IF-THEN

IF boolean-expression THEN
    statements
END IF;

IF-THEN 语句是 IF 最简单的形式。条件为真时,THEN 和 END IF 之间的语句将被执行。否则,它们被跳过。

IF v_user_id <> 0 THEN
    UPDATE users SET email = v_email WHERE user_id = v_user_id;
END IF;

19.6.2.2. IF-THEN-ELSE

IF boolean-expression THEN
    statements
ELSE
    statements
END IF;

IF-THEN-ELSE 语句在 IF-THEN 的基础上增加了对条件求值为假时应执行 的另一组语句的指定。

IF parentid IS NULL or parentid = ''''
THEN 
    return fullname;
ELSE
    return hp_true_filename(parentid) || ''/'' || fullname;
END IF;


IF v_count > 0 THEN 
    INSERT INTO users_count(count) VALUES(v_count);
    return ''t'';
ELSE 
    return ''f'';
END IF;

19.6.2.3. IF-THEN-ELSE IF

IF 语句可以嵌套,如下例所示:

IF demo_row.sex = ''m'' THEN
  pretty_sex := ''man'';
ELSE
  IF demo_row.sex = ''f'' THEN
    pretty_sex := ''woman'';
  END IF;
END IF;

使用这种形式时,你实际上是在外层 IF 语句的 ELSE 部分中嵌套了一个 IF 语句。因此,每个嵌套的 IF 需要一个 END IF 语句,父级 IF-ELSE 也需要一个。这可行,但当要检查的备选很多时就变得乏味。

19.6.2.4. IF-THEN-ELSIF-ELSE

IF boolean-expression THEN
    statements
[ ELSIF boolean-expression THEN
    statements
[ ELSIF boolean-expression THEN
    statements
    ...]]
[ ELSE
    statements ]
END IF;

IF-THEN-ELSIF-ELSE 提供了在一条语句中检查许多备选的 更方便的方法。形式上它等价于嵌套的 IF-THEN-ELSE-IF-THEN 命令,但只需要一个 END IF。

下面是一个例子:

IF number = 0 THEN
    result := ''zero'';
ELSIF number > 0 THEN 
    result := ''positive'';
ELSIF number < 0 THEN
    result := ''negative'';
ELSE
    -- hmm, the only other possibility is that number IS NULL
    result := ''NULL'';
END IF;

最后的 ELSE 部分是可选的。

19.6.3. 简单循环 #

使用 LOOP、EXIT、WHILE 和 FOR 语句,你可以让 PL/pgSQL 函数重复执行一系列命令。

19.6.3.1. LOOP

[<<label>>]
LOOP
    statements
END LOOP;

LOOP 定义一个无限重复的无条件循环,直到被 EXIT 或 RETURN 语句 终止。可选的标签可供嵌套循环中的 EXIT 语句用来指定应终止哪一层 嵌套。

19.6.3.2. EXIT

EXIT [ label ] [ WHEN expression ];

如果没有给出 label,最内层的循环被 终止,接下来执行 END LOOP 之后的语句。如果给出了 label,它必须是当前层或某个外层嵌套 循环或块的标签。然后命名的循环或块被终止,控制继续到该循环/块 对应的 END 之后的语句。

如果存在 WHEN,只有当指定条件为真时才退出循环,否则控制传递到 EXIT 之后的语句。

例子:

LOOP
    -- some computations
    IF count > 0 THEN
        EXIT;  -- exit loop
    END IF;
END LOOP;

LOOP
    -- some computations
    EXIT WHEN count > 0;
END LOOP;

BEGIN
    -- some computations
    IF stocks > 100000 THEN
        EXIT;  -- illegal. Can't use EXIT outside of a LOOP
    END IF;
END;

19.6.3.3. WHILE

[<<label>>]
WHILE expression LOOP
    statements
END LOOP;

WHILE 语句在条件表达式求值为真期间重复一个语句序列。条件在每次 进入循环体之前检查。

例如:

WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
    -- some computations here
END LOOP;

WHILE NOT boolean_expression LOOP
    -- some computations here
END LOOP;

19.6.3.4. FOR(整数 for 循环)

[<<label>>]
FOR name IN [ REVERSE ] expression .. expression LOOP
    statements
END LOOP;

这种形式的 FOR 创建一个在整数值范围上迭代的循环。变量 name 自动定义为 integer 类型,且只存在 于循环内部。给出范围下界和上界的两个表达式在进入循环时求值一次。 迭代步长通常为 1,指定 REVERSE 时为 -1。

整数 FOR 循环的一些例子:

FOR i IN 1..10 LOOP
  -- some expressions here

    RAISE NOTICE ''i is %'',i;
END LOOP;

FOR i IN REVERSE 10..1 LOOP
    -- some expressions here
END LOOP;

19.6.4. 遍历查询结果 #

使用另一种类型的 FOR 循环,你可以遍历查询的结果并相应地操纵那些 数据。语法为:

[<<label>>]
FOR record | row IN select_query LOOP
    statements
END LOOP;

记录或行变量被相继赋以 SELECT 查询产生的所有行,并对每一行执行 循环体。下面是一个例子:

CREATE FUNCTION cs_refresh_mviews () RETURNS INTEGER AS '
DECLARE
     mviews RECORD;
BEGIN
     PERFORM cs_log(''Refreshing materialized views...'');

     FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP

         -- Now "mviews" has one record from cs_materialized_views

         PERFORM cs_log(''Refreshing materialized view '' || quote_ident(mviews.mv_name) || ''...'');
         EXECUTE ''TRUNCATE TABLE  '' || quote_ident(mviews.mv_name);
         EXECUTE ''INSERT INTO '' || quote_ident(mviews.mv_name) || '' '' || mviews.mv_query;
     END LOOP;

     PERFORM cs_log(''Done refreshing materialized views.'');
     RETURN 1;
end;
' LANGUAGE 'plpgsql';

如果循环被 EXIT 语句终止,最后赋的行值在循环之后仍可访问。

FOR-IN-EXECUTE 语句是遍历记录的另一种方式:

[<<label>>]
FOR record | row IN EXECUTE text_expression LOOP 
    statements
END LOOP;

这与前面的形式类似,不同之处在于源 SELECT 语句是作为字符串表达式 指定的,它在每次进入 FOR 循环时被求值并重新规划。这使程序员可以在 预规划查询的速度和动态查询的灵活性之间做出选择,就像普通的 EXECUTE 语句一样。

注意

PL/pgSQL 解析器目前通过检查 FOR 之后提到的目标变量 是否被声明为记录/行变量来区分两类 FOR 循环(整数或返回记录)。 如果不是,就推定为整数 FOR 循环。当真正的问题比如说只是 FOR 变量名拼错时,这可能导致相当不直观的错误消息。

提交更正

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