块中使用的所有变量都必须在该语句块的声明部分中声明。(唯一的例外是,针对一系列整数值进行迭代操作的 FOR
循环的循环变量被自动声明为 integer 变量,同样,针对游标结果进行迭代操作的 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
[ COLLATEcollation_name
] [ NOT NULL ] [ { DEFAULT | := | = }expression
];
如果给出了 DEFAULT
子句,则指定在进入块时分配给该变量的初始值。如果未给出 DEFAULT
子句,则将变量初始化为SQL空值。 CONSTANT
选项禁止在初始化后将变量赋值,以便在块持续期间其值将保持不变。 COLLATE
选项指定用于变量的校对(请参阅 第 41.3.6 节)。如果指定 NOT NULL
,则分配空值会导致运行时出错。声明为 NOT NULL
的所有变量都必须指定非空默认值。可以使用等于 (=
) 代替符合 PL/SQL 的 :=
。
每次进入该块(而不仅仅是每次函数调用)时,变量的默认值都会被计算出来并赋值给变量。因此,例如将 now()
赋值给类型为 timestamp
的变量会导致该变量具有当前函数调用的时间,而不是函数预编译时的时间。
示例
quantity integer DEFAULT 32; url varchar := 'http://mysite.com'; transaction_time CONSTANT timestamp with time zone := now();
声明后,可以在同一块中的其他初始化表达式中使用该变量的值,例如
DECLARE x integer := 1; y integer := x + 1;
传递给函数的参数用标识符 $1
、$2
等命名。作为可选项,可以针对 $
参数名称声明别名以增加可读性。然后可以使用该别名或数字标识符来引用参数值。n
有两种创建别名的方式。首选方法是在 CREATE FUNCTION
命令中为参数指定一个名称,例如
CREATE FUNCTION sales_tax(subtotal real) RETURNS real AS $$ BEGIN RETURN subtotal * 0.06; END; $$ LANGUAGE plpgsql;
另一种方法是使用声明语法显式地声明别名
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;
这两个示例并不完全等效。在第一个示例中,可以将 subtotal
引用为 sales_tax.subtotal
,但在第二个示例中则不行。(如果我们向内部块附加了一个标签,则可以对 subtotal
使用该标签限定,而不是这样做。)
更多示例
CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$ DECLARE v_string ALIAS FOR $1; index ALIAS FOR $2; BEGIN -- some computations using v_string and index here 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 函数使用输出参数声明时,会以与普通输入参数相同的方式向输出参数指定 $
名称和可选别名。输出参数实际上是一个以 NULL 开始的变量;它应该在函数执行期间分配。参数的最终值即为返回的值。例如,还可以通过这种方式完成销售税示例n
CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$ BEGIN tax := subtotal * 0.06; END; $$ LANGUAGE plpgsql;
请注意,我们省略了 RETURNS real
— 我们本可以包含它,但这将是多余的。
要调用带有 OUT
参数的函数,请在函数调用中省略输出参数
SELECT sales_tax(100.00);
在返回多个值时,输出参数最有用。一个简单的示例是
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; SELECT * FROM sum_n_product(2, 4); sum | prod -----+------ 6 | 8
正如 第 36.5.4 节 中所述,此操作实际上为函数结果创建了一个匿名记录类型。如果给出了 RETURNS
子句,则它必须说明 RETURNS record
。
这也可以与过程配合使用,例如
CREATE PROCEDURE sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$ BEGIN sum := x + y; prod := x * y; END; $$ LANGUAGE plpgsql;
在对过程的调用中,必须指定所有参数。对于输出参数,从纯 SQL 调用过程时可以指定 NULL
CALL sum_n_product(2, 4, NULL, NULL); sum | prod -----+------ 6 | 8
然而,从 PL/pgSQL 调用过程时,你应该改为为任何输出参数编写一个变量;该变量将接收调用的结果。有关详细信息,请参见 第 41.6.3 节。
声明 PL/pgSQL 函数的另一种方法是使用 RETURNS TABLE
,例如
CREATE FUNCTION extended_sales(p_itemno int) RETURNS TABLE(quantity int, total numeric) AS $$ BEGIN RETURN QUERY SELECT s.quantity, s.quantity * s.price FROM sales AS s WHERE s.itemno = p_itemno; END; $$ LANGUAGE plpgsql;
这完全等效于声明一个或多个 OUT
参数并指定 RETURNS SETOF
。sometype
在 PL/pgSQL 函数的返回类型被声明为多态类型时(请参见 第 36.2.5 节),将创建一个特殊参数 $0
。其数据类型是从实际输入类型推导出的函数的实际返回类型。这样函数便可访问其实际返回类型,如 第 41.3.3 节 中所示。 $0
初始化为 null,并且函数可以修改它,所以如果需要的话,可以使用它保存返回值,但这不是必需的。 $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;
通过将一个或多个输出参数声明为多态类型,也可以达到相同的效果。在这种情况下,不使用特殊 $0
参数;输出参数自身服务于相同用途。例如
CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement, OUT sum anyelement) AS $$ BEGIN sum := v1 + v2 + v3; END; $$ LANGUAGE plpgsql;
在实践中,使用 anycompatible
类型系列声明多态函数可能更有用,以便自动将输入参数提升为公用类型。例如
CREATE FUNCTION add_three_values(v1 anycompatible, v2 anycompatible, v3 anycompatible) RETURNS anycompatible AS $$ BEGIN RETURN v1 + v2 + v3; END; $$ LANGUAGE plpgsql;
对于此示例,诸如
SELECT add_three_values(1, 2, 4.7);
的调用将起作用,自动将整数输入提升为数字。使用 anyelement
的函数要求您手动将三个输入强制转换为同一种类型。
ALIAS
#newname
ALIAS FORoldname
;
与上一节中建议的不同,ALIAS
语法更为通用:您可以为任何变量声明别名,而不仅仅是函数参数。对此的主要实际用途是为具有预定名称的变量分配一个不同的名称,例如触发函数中的 NEW
或 OLD
。
示例
DECLARE prior ALIAS FOR old; updated ALIAS FOR new;
由于 ALIAS
为同一对象创建两种不同的命名方式,因此不受限制的使用会导致混淆。最好仅将其用于覆盖预定名称的目的。
name
table
.column
%TYPEname
variable
%TYPE
%TYPE
提供表列的数据类型或先前声明的 PL/pgSQL 变量。您可以使用此方法声明将包含数据库值的变量。例如,假设您在 users
表中有名为 user_id
的列。要声明与 users.user_id
具有相同数据类型的变量,可以编写
user_id users.user_id%TYPE;
还可以写 %TYPE
之后的数组装饰,从而创建包含引用类型数组的变量
user_ids users.user_id%TYPE[]; user_ids users.user_id%TYPE ARRAY[4]; -- equivalent to the above
与声明数组表列时一样,无论您写入多组括号对还是特定的数组维度都无关紧要:PostgreSQL 将指定元素类型的数组都视为相同的类型,无论其维度如何。(请参阅第 8.15.1 节。)
由于使用了 %TYPE
,所以您无需知道您正在引用的结构的数据类型,更重要的是,如果引用的项目的数据类型在将来发生更改(比如,您将 user_id
的类型从 integer
更改为 real
),那么您可能无需更改您的函数定义。
%TYPE
在多态函数中极具价值,因为内部变量所需数据类型在一次调用和另一次调用之间可能发生变化。可以使用 %TYPE
对函数参数或结果占位符进行应用来创建合适的变量。
name
table_name
%ROWTYPE
;name
composite_type_name
;
复合类型的变量被称为行变量(或行类型变量)。该变量可以容纳 SELECT
或 FOR
查询结果的整行,只要该查询的列集与变量的已声明类型相匹配。单个行值的字段使用通常的点语法进行访问,例如 rowvar.field
。
可以使用 table_name
%ROWTYPE
表示法将行变量声明为与现有表或视图的行相同的类型;或者可通过提供复合类型的名称进行声明。(由于每个表都具有同名的关联复合类型,因此,在 PostgreSQL 中是否写入 %ROWTYPE
实际上无关紧要。但使用 %ROWTYPE
的形式更便于移植。)
与 %TYPE
一样,%ROWTYPE
后面可以接数组修饰来声明容纳引用的复合类型数组的变量。
函数参数可以是复合类型(完整的表行)。在这种情况下,相应的标识符 $
将是行变量,并且可以从中选择字段,例如 n
$1.user_id
。
以下是使用复合类型的示例。 table1
和 table2
是至少具有以下字段的现有表
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 ... ;
name
RECORD;
记录变量与行类型变量类似,但它们没有预定义的结构。它们采用在 SELECT
或 FOR
命令中分配给它们的行的实际行结构。当每次被分配给记录变量时,该记录变量的子结构可能会发生变化。由此产生的后果是,在首次分配记录变量之前,它没有任何子结构,并且尝试访问其中的字段会导致运行时错误。
请注意 RECORD
不是一个真正的数据类型,而只是一个占位符。还需要了解的是,当声明 PL/pgSQL 函数返回类型 record
时,这与记录变量的概念并不完全相同,尽管此类函数可能会使用记录变量来保存其结果。在这两种情况下,在编写函数时都无法得知实际的行结构,但是对于返回 record
的函数,在解析调用查询时确定实际结构,而记录变量可以动态地更改其行结构。
当 PL/pgSQL 函数具有一个或多个可排序数据类型的参数时,会为每个函数调用标识一个排序规则,具体取决于分配给实际参数的排序规则,如 第 23.2 节 所述。如果成功标识排序规则(即,参数之间没有隐式排序规则的冲突),则所有可排序参数都隐式地被视为具有该排序规则。这将影响函数中对排序规则敏感的操作的行为。例如,请考虑
CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$ BEGIN RETURN a < b; END; $$ LANGUAGE plpgsql; SELECT less_than(text_field_1, text_field_2) FROM table1; SELECT less_than(text_field_1, text_field_2 COLLATE "C") FROM table1;
第一次使用 less_than
将针对 text_field_1
和 text_field_2
的共同排序规则进行比较,而第二次使用将使用 C
排序规则。
此外,标识的排序规则也被假定为任何可排序类型局部变量的排序规则。因此,如果将此函数写为
CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$ DECLARE local_a text := a; local_b text := b; BEGIN RETURN local_a < local_b; END; $$ LANGUAGE plpgsql;
如果不存在可排序数据类型的参数,或者无法为它们标识出共同的排序规则,则参数和局部变量使用它们的数据类型(通常是数据库的默认排序规则,但对于域类型变量可能不同)的默认排序规则。
可排序数据类型的局部变量可以通过在声明中包含 COLLATE
选项来关联不同的排序规则,例如
DECLARE local_a text COLLATE "en_US";
此选项会覆盖根据上述规则分配给变量的排序规则。
当然,如果希望在特定操作中强制使用特定校对,也可以在函数内部编写显式的COLLATE
子句。例如:
CREATE FUNCTION less_than_c(a text, b text) RETURNS boolean AS $$ BEGIN RETURN a < b COLLATE "C"; END; $$ LANGUAGE plpgsql;
这将覆盖与表达式中使用的表列、参数或局部变量关联的校对,就像在纯 SQL 命令中发生的那样。