服务器 频道

PL/pgSQL控制结构

 

    使用不同类型的FOR循环,你可以遍历一个命令的结果并且相应的操作哪些数据。语法是:

[<<label>>]
FOR record_or_row IN query LOOP
    statements
END LOOP;
    这里的记录或者行变量将相继被赋予所有来自query(必须是一条 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

         -- 现在 "mviews" 里有了一条来自 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(mview.mv_name) || '' '' || mview.mv_query;
     END LOOP;

     PERFORM cs_log(''Done refreshing materialized views.'');
     RETURN 1;
END;
$$ LANGUAGE plpgsql;
    如果循环是用一个EXIT语句终止的,那么在循环之后你仍然可以访问最后赋值的行。

FOR-IN-EXECUTE语句是遍历所有行的另外一种方法:

[<<label>>]
FOR record_or_row IN EXECUTE text_expression LOOP
    statements
END LOOP;
    这个例子类似前面的形式,只不过源SELECT语句声明为了一个字串表达式, 这样它在每次进入FOR循环的时候都会重新计算和生成执行计划。 这样就允许程序员在一个预先规划好了的命令所获得的速度,和一个动态命令所获得的灵活性(就象一个简单的EXECUTE语句那样)之间进行选择。

    注意: PL/pgSQL 分析器目前区分两种类型的FOR循环(整数或者返回记录的): 方法是检查是否有任何 .. 出现在 IN 和 LOOP 之间的圆括弧之外。 如果没有看到 ..,那么这个循环就是在数据行上的循环。 如果误敲了 .. 就很可能会导致像下面这样的错误信息: "loop variable of loop over rows must be a record or row variable", 而不是我们以为会看到的简单的语法错误。

    捕获错误

    缺省时,一个在 PL/pgSQL 函数里发生的错误退出函数的执行, 并且实际上是其周围的事务也会退出。你可以使用一个带有 EXCEPTION 子句的 BEGIN 块捕获错误并且从中恢复。 其语法是正常的 BEGIN 块语法的一个扩展:

[ <<label>> ]
[ DECLARE
    declarations ]
BEGIN
    statements
EXCEPTION
    WHEN condition [ OR condition ... ] THEN
        handler_statements
    [ WHEN condition [ OR condition ... ] THEN
          handler_statements
      ... ]
END;

    如果没有发生错误,这种形式的块只是简单地执行所有 statements, 但是如果在 statements 里发生了一个错误, 则对 statements 的进一步处理将废弃, 控制传递到了 EXCEPTION 列表。 系统搜索这个列表,寻找匹配发生的错误的第一个元素。如果找到匹配, 则执行对应的 handler_statements,然后控制传递到 END 之后的下一个语句。 如果没有找到匹配,该错误就会广播出去,就好像根本没有 EXCEPTION 子句一样: 该错误可以被一个包围块用 EXCEPTION 捕获,如果没有包围块,则退出函数的处理。

    condition 名字可以是 Appendix A 里显示的任何名字。 一个范畴名匹配任意该范畴里的错误。特殊的条件名 OTHERS 匹配除了 QUERY_CANCELED 之外的所有错误类型。 (我们可以用名字捕获 QUERY_CANCELED,不过通常是不明智的。)条件名是大小写无关的。

    如果在选中的 handler_statements 里发生了新错误, 那么它不能被这个 EXCEPTION 子句捕获,而是传播出去。 一个外层的 EXCEPTION 子句可以捕获它。

    如果一个错误被 EXCEPTION 捕获,PL/pgSQL 函数的局部变量保持错误发生的时候的原值, 但是所有该块中想固化在数据库中的状态都回滚。作为一个例子,让我们看看下面片断:

    INSERT INTO mytab(firstname, lastname) VALUES(''Tom'', ''Jones'');
    BEGIN
        UPDATE mytab SET firstname = ''Joe'' WHERE lastname = ''Jones'';
        x := x + 1;
        y := x / 0;
    EXCEPTION
        WHEN division_by_zero THEN
            RAISE NOTICE ''caught division_by_zero'';
            RETURN x;
    END;

    当控制到达给 y 赋值的地方的时候,它会带着一个 division_by_zero 错误失败。 这个错误将被 EXCEPTION 子句波获。而在 RETURN 语句里返回的数值将是 x 的增量值。 但是,在该块之前的 INSERT 将不会回滚,因此最终的结果是数据库包含 Tom Jones 而 不是 Joe Jones。

    提示: 进入和退出一个包含 EXCEPTION 子句的块要比不包含的块开销大的多。 因此,不必要的时候不要使用 EXCEPTION。

 

0
相关文章