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

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

23.6. 控制结构 #

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

23.6.1. 从函数返回 #

RETURN expression;

函数终止,expression的值会被返回给 上层执行器。表达式的结果会被自动转换成函数的返回类型, 转换方式与赋值中描述的相同。

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

23.6.2. 条件语句 #

IF语句让你可以基于某些条件执行命令。 PL/pgSQL有四种 IF 形式:IF-THEN、 IF-THEN-ELSE、IF-THEN-ELSE IF 和 IF-THEN-ELSIF-THEN-ELSE。

23.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;

23.6.2.2. IF-THEN-ELSE

IF boolean-expression THEN
    statements
ELSE
    statements
END IF;

IF-THEN-ELSE 语句在 IF-THEN 的基础上增加了一种能力:你可以 指定一组在条件求值为 FALSE 时执行的备选语句。

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;

23.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 语句嵌套在外层 IF 语句 的 ELSE 部分中。因此每个嵌套的 IF 都需要一个 END IF 语句, 外层的 IF-ELSE 也需要一个。这是可行的,但当要检查的备选 分支很多时就会变得乏味。

23.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 部分是可选的。

23.6.3. 简单循环 #

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

23.6.3.1. LOOP

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

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

23.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;

23.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;

23.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;

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