返回介绍

I. 教程

II. SQL 语言

III. 服务器管理

IV. 客户端接口

V. 服务器端编程

VI. 参考手册

VII. 内部

VIII. 附录

37.4. 声明

发布于 2019-09-30 03:09:27 字数 6365 浏览 916 评论 0 收藏 0

所有在块里使用的变量都必须在一个块的声明段里声明。唯一的例外是一个 FOR 循环里的循环变量是在一个整数范围内迭代的,被自动声明为整数变量。

PL/pgSQL 变量可以使用任意的 SQL 数据类型,比如 integer, varchar, char 等等。

下面是一些变量声明的例子:

user_id integer;
quantity numeric(5);
url varchar;
myrow tablename%ROWTYPE;
myfield tablename.columnname%TYPE;
arow RECORD;

一个变量声明的一般性语法是:

name [CONSTANT] type [NOT NULL] [{ DEFAULT | := } expression];

如果给出了 DEFAULT 子句,那么它声明了在进入该块的时候赋予该变量的初始值。如果没有给出 DEFAULT 子句,那么该变量初始化为 NULL 。CONSTANT 选项避免了该变量被赋值,这样其数值在该块的范围内保持常量。如果声明了 NOT NULL ,那么赋予 NOT NULL 数值将导致一个运行时错误。所以所有声明为 NOT NULL 的变量还必须声明一个非空的缺省值。

缺省值是在每次进入该块的时候计算的。因此,如果把 now() 赋予一个类型为 timestamp 的变量会令变量拥有函数实际调用的时间,而不是函数预编译的时间。例如:

quantity integer DEFAULT 32;
url varchar := 'http://mysite.com';
user_id CONSTANT integer := 10;

37.4.1. 函数参数的别名

传递给函数的参数都是用 $1, $2 等等这样的标识符。为了增加可读性,可以为 $n 参数名声明别名。然后别名或者数字标识符都可以指向参数值。

有两种创建别名的方法,比较好的是在 CREATE FUNCTION 命令里给出参数名,比如:

CREATE FUNCTION sales_tax(subtotal real) RETURNS real AS $$
BEGIN
    RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

另外一个方法,是 PostgreSQL 8.0 以前的唯一的方法,是明确地声明为别名,使用声明语法

name ALIAS FOR $n;

这个风格的同一个例子看起来像下面这样

CREATE FUNCTION sales_tax(real) RETURNS real AS $$
DECLARE
    subtotal ALIAS FOR $1;
BEGIN
    RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

更多例子:

CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$
DECLARE
    v_string ALIAS FOR $1;
    index ALIAS FOR $2;
BEGIN
    -- 这里放一些使用 v_string 和 index 的计算
END;
$$ LANGUAGE plpgsql;


CREATE FUNCTION concat_selected_fields(in_t sometablename) RETURNS text AS $$
BEGIN
    RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
$$ LANGUAGE plpgsql;

如果一个 PL/pgSQL 函数声明中含有输出参数,那么就会给予输出参数 $n 的名字以及可选的别名,方法和其它正常输入参数一样。一个输出参数实际上是初始值为 NULL 的变量;在函数执行的过程中,应该给它赋值。该参数的最后数值是返回的东西。比如,销售额-税费的例子也可以这么做:

CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$
BEGIN
    tax := subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

请注意忽略了 RETURNS real ,当然也可以包含它,不过这样就显得多余了。

输出参数在返回多个数值的时候非常有用。一个简单的例子是:

CREATE FUNCTION sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$
BEGIN
    sum := x + y;
    prod := x * y;
END;
$$ LANGUAGE plpgsql;

正如在节33.4.3里面讨论的,这样做实际上为函数的结果创建了一个匿名的记录类型。如果给出一个 RETURNS 子句,那么它就必须使用 RETURNS record

如果将 PL/pgSQL 函数的返回类型声明为多态类型(anyelementanyarray),那么就会创建一个特殊的 $0 参数,它的数据类型是函数的实际返回类型,和从实际输入类型的推导类型一样(参阅节33.2.5)。这样就允许函数像节37.4.2里显示的那样访问它的实际返回类型。$0 初始化为空,并且可以被函数修改,所以,如果需要,它可以用于保存返回值,虽然这并非必须。$0 还可以给予一个别名。比如,这个函数可以在任何有 + 操作符的数据类型上运转:

CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement)
RETURNS anyelement AS $$
DECLARE
    result ALIAS FOR $0;
BEGIN
    result := v1 + v2 + v3;
    RETURN result;
END;
$$ LANGUAGE plpgsql;

定义一个或者多个参数为 anyelementanyarray 也可以实现同样的效果。在这种情况下,特殊的参数 $0 不会使用;输出参数自己起这个作用。比如:

CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement,
                                 OUT sum anyelement)
AS $$
BEGIN
    sum := v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;

37.4.2. 拷贝类型

variable%TYPE

%TYPE 提供一个变量或者表字段的数据类型。你可以用这个声明将要保存数据库数值的变量。比如,假如你在 users 表里面有一个 user_id 字段。要声明一个和 users.user_id 类型相同的变量,可以这样写:

user_id users.user_id%TYPE;

通过使用 %TYPE ,你无需知道引用的结构的数据类型,并且,最重要的是,如果被引用项的数据类型在将来变化了(比如把 user_id 的类型从 integer 改成 real),也不需要修改函数定义。

%TYPE 对多态函数特别有用,因为内部变量的数据类型可能在不同调用中不一样。可以通过给函数的参数或者结果占位符附加 %TYPE 的方法来创建合适的变量。

37.4.3. 行类型

name table_name%ROWTYPE;
name composite_type_name;

一个复合类型变量叫做变量(或者 row-type 变量)。这样的一个变量可以保存一次 SELECTFOR 命令结果的完整一行,只要命令的字段集匹配该变量声明的类型。行数值的字段使用点表示法访问,比如 rowvar.field

行变量可以声明为和一个现有的表或者视图的行类型相同,方法是使用 table_name%ROWTYPE 表示法;或者你也可以声明它的类型是一个复合类型的名字。因为每个表都有一个相关联的同名数据类型,在 PostgreSQL 里实在是无所谓你写不写 %ROWTYPE 。但是有 %ROWTYPE 的形式移植性更好。

函数的参数可以是复合类型(表的完整行)。这个时候,对应的标识符 $n 将是一个行变量,并且可以从中选取字段,比如 $1.user_id

在一个行类型的变量中,只可以访问用户定义的表中行的属性,不包括 OID 或者其它系统属性(因为该行可能来自一个视图)。该行类型的数据域继承表中像 char(n) 这种类型字段的尺寸和精度。

这里是一个使用复合类型的例子。table1table2 是现有的表,至少包含代码中提到的字段:

CREATE FUNCTION merge_fields(t_row table1) RETURNS text AS $$
DECLARE
    t2_row table2%ROWTYPE;
BEGIN
    SELECT * INTO t2_row FROM table2 WHERE ... ;
    RETURN t_row.f1 || t2_row.f3 || t_row.f5 || t2_row.f7;
END;
$$ LANGUAGE plpgsql;

SELECT merge_fields(t.*) FROM table1 t WHERE ... ;

37.4.4. 记录类型

name RECORD;

纪录变量类似行类型变量,但是它们没有预定义的结构。它们在 SELECTFOR 命令中获取实际的行结构。一个行变量的子结构可以在每次赋值的时候改变。这样做的一个结果是:在一个记录变量被赋予数值之前,它没有子结构,并且任何对其中的数据域进行访问的企图都将产生一个运行时错误。

请注意,RECORD 不是真正的数据类型,只是一个占位符。还应该意识到在把一个 PL/pgSQL 函数声明为返回 record 类型的时候,它和一个记录变量的概念并不完全相同,即使这个函数可能使用一个记录变量保存它的结果也如此。在这两种情况下书写函数的时候,实际的行结构都是未知的,但是对于返回 record 的函数来说,实际的结构是在调用它的查询被分析的时候决定的,而行变量可以在运行中改变其行结构。

37.4.5. RENAME

RENAME oldname TO newname;

可以用 RENAME 声明修改一个变量、记录、行的名字。如果 NEWOLD 在个触发器过程里被另外一个名字引用,那么这个东西就很有用。又见 ALIAS

例子:

RENAME id TO user_id;
RENAME this_var TO that_var;

【注意】 RENAME 在 PostgreSQL 7.3 里好像有问题。修补这个毛病的优先级比较低,因为 ALIAS 覆盖了大多数 RENAME 的实际用途。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文