19.3. 声明

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

PL/pgSQL变量可以由任意的 SQL 数据类型,比如 INTEGERVARCHARCHAR

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

user_id INTEGER;
quantity NUMERIC(5);
url VARCHAR;
myrow tablename%ROWTYPE;
myfield tablename.fieldname%TYPE;
arow RECORD;

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

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

如果给出了 DEFAULT 子句,那么它声明了在进入该块的时候 赋予该变量的初始值.如果没有给出 DEFAULT 子句,那么该变量初始化为 SQL NULL 值.

CONSTANT 选项避免了该变量被赋值,这样其数值在该块的范围内保持常量. 如果声明了 NOT NULL,那么赋予 NULL 数值将导致一个运行时错误. 所以所有声明为 NOT NULL 的变量还必须声明一个非 NULL 的缺省值.

缺省值是在每次进入该块的时候计算的.因此,举例来说, 把'now'赋予一个类型为 TIMESTAMP 的变量会令变量拥有函数实际调用 的时间,而不是函数预编译的时间.

例子∶

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

19.3.1. 函数参数的别名

name ALIAS FOR $n;

传递给函数的参数都是用 $1$2, 等等这样的标识符.为了增加可读性,我们可以为 $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
    -- 这里放一些计算
END;
' LANGUAGE 'plpgsql';

CREATE FUNCTION use_many_fields(tablename) RETURNS TEXT AS '
DECLARE
    in_t ALIAS FOR $1;
BEGIN
    RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
' LANGUAGE 'plpgsql';

19.3.2. 行类型

name tablename%ROWTYPE;

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

目前,一个行变量只能用 %ROWTYPE 表示法声明; 尽管我们可能认为一个光表名也可以做类型声明用,但在 PL/pgSQL 函数里是不行的.

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

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

CREATE FUNCTION use_two_tables(tablename) RETURNS TEXT AS '
DECLARE
    in_t ALIAS FOR $1;
    use_t table2name%ROWTYPE;
BEGIN
    SELECT * INTO use_t FROM table2name WHERE ... ;
    RETURN in_t.f1 || use_t.f3 || in_t.f5 || use_t.f7;
END;
' LANGUAGE 'plpgsql';

19.3.3. 记录

name RECORD;

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

请注意 RECORD 不是真正的数据类型,只是一个占位符.

19.3.4. 属性

使用 %TYPE%ROWTYPE 属性, 你可以把变量声明为与另外一个数据库项相同的数据类型或结构(比如∶ 一个表的域).

variable%TYPE

%TYPE 给我们提供了一个变量或者数据库列的数据类型. 你可以用这个关键字声明那些需要保存数据库数值的变量. 举例来说,让我们假设你有一个叫 user_id 的列 在你的 users 表里.要声明一个和 users.user_id 相同的数据类型, 你可以∶

user_id	users.user_id%TYPE;

通过使用 %TYPE,你就不需要知道你引用的 结构的数据类型,最重要的是,如果被引用项的数据类型在将来 变化了(比如你把表定义列的 user_id 从 INTEGER 改成 REAL),那你也 用不着修改你的函数定义.

table%ROWTYPE

%ROWTYPE 提供了一个对应特定表的一整行的复合类型. table 必须 是该数据库里一个现存的表或者视图的名字.

DECLARE
	users_rec users%ROWTYPE;
	user_id users.user_id%TYPE;
BEGIN
    user_id := users_rec.user_id;
    ...

CREATE FUNCTION does_view_exist(INTEGER) RETURNS bool AS '
   DECLARE
        key ALIAS FOR $1;
        table_data cs_materialized_views%ROWTYPE;
   BEGIN
        SELECT INTO table_data * FROM cs_materialized_views
               WHERE sort_key=key;

        IF NOT FOUND THEN
           RETURN false;
        END IF;
        RETURN true;
   END;
' LANGUAGE 'plpgsql';

19.3.5. RENAME

RENAME oldname TO newname;

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

例子∶

RENAME id TO user_id;
RENAME this_var TO that_var;

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