pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
在本节及随后几节中,我们描述 PL/pgSQL 显式 理解的所有语句类型。凡是没有被识别为这些语句类型之一的内容,都被推定为 SQL 命令,并被发送到主数据库引擎执行(在此之后,语句中使用的任何 PL/pgSQL 变量都会被替换)。因此,例如 SQL 命令 INSERT、UPDATE 和 DELETE 可以 被视为 PL/pgSQL 的语句,但这里不专门列出。
向变量或行/记录字段赋值写作:
identifier:=expression;
如上文所述,这种语句中的表达式通过向主数据库引擎发送 SQL SELECT 命令来求值。表达式必须产生单个值。
如果表达式的结果数据类型与变量的数据类型不匹配,或者变量有特定的大小/ 精度(比如 char(20)),结果值将由 PL/pgSQL 解释器使用结果类型的输出函数和 变量类型的输入函数隐式转换。注意,如果结果值的字符串形式不被输入 函数接受,这可能潜在地导致输入函数产生运行时错误。
例子:
user_id := 20; tax := subtotal * 0.06;
SELECT INTO #产生多列(但只有一行)的 SELECT 命令的结果可以赋给 一个记录变量、行类型变量或标量变量列表。写法是:
SELECT INTOtargetselect_expressionsFROM ...;
其中 target 可以是记录变量、行变量,或由 简单变量和记录/行字段组成的逗号分隔列表。 select_expressions 和命令的其余部分与普通 SQL 中相同。
注意,这与 PostgreSQL 对 SELECT INTO 的正常解释大不相同——后者的 INTO 目标是一张新建的表。如果 你想在 PL/pgSQL 函数内部从 SELECT 结果创建表,请使用语法 CREATE TABLE ... AS SELECT。
如果用一行或一个变量列表作为目标,选出的值必须与目标的结构完全匹配, 否则会发生运行时错误。当目标是记录变量时,它会自动把自己配置为查询 结果列的行类型。
除了 INTO 子句之外,SELECT 语句与普通 SQL SELECT 命令相同,可以使用其全部能力。
如果查询返回零行,会向目标赋予空值。如果查询返回多行,第一行被赋给 目标,其余的都被丢弃。(注意,除非你使用了 ORDER BY, 否则“第一行”没有明确的定义。)
目前,INTO 子句几乎可以出现在 SELECT 语句的任何位置,但建议像上面描述的那样把它紧放在 SELECT 关键字之后。PL/pgSQL 的未来版本可能对 INTO 子句的位置不那么宽容。
你可以在 SELECT INTO 语句之后立即使用 FOUND 来判断赋值是否成功(即查询是否至少返回了一行)。 例如:
SELECT INTO myrec * FROM emp WHERE empname = myname;
IF NOT FOUND THEN
RAISE EXCEPTION ''employee % not found'', myname;
END IF;
要测试记录/行结果是否为空,可以使用 IS NULL 条件。 但没有办法知道是否有额外的行被丢弃。下面是一个处理未返回行情况的 例子:
DECLARE
users_rec RECORD;
full_name varchar;
BEGIN
SELECT INTO users_rec * FROM users WHERE user_id=3;
IF users_rec.homepage IS NULL THEN
-- user entered no homepage, return "http://"
RETURN ''http://'';
END IF;
END;
有时人们希望求一个表达式或查询的值但丢弃其结果(通常是因为正在调用 一个有有用副作用但没有有用结果值的函数)。在 PL/pgSQL 中要做到这一点,使用 PERFORM 语句:
PERFORM query;
这会执行 query(它必须是一条 SELECT 语句)并丢弃结果。 PL/pgSQL 变量照常在查询中被替换。此外, 如果查询产生了至少一行,特殊变量 FOUND 会被设置为 真;如果没有产生行,则为假。
人们可能以为不带 INTO 子句的 SELECT 能达到这个效果,但目前唯一被接受的方式是 PERFORM。
一个例子:
PERFORM create_mv(''cs_session_page_requests_mv'', my_query);
很多时候你会想在 PL/pgSQL 函数内部生成 动态命令,也就是每次执行时会涉及不同的表或不同数据类型的命令。 PL/pgSQL 为命令缓存计划的常规做法在这种 场景下行不通。为处理这类问题,提供了 EXECUTE 语句:
EXECUTE command-string;
其中 command-string 是一个产生字符串 (类型为 text)的表达式,该字符串包含要执行的命令。 这个字符串被按字面送给 SQL 引擎。
特别注意,命令字符串不做 PL/pgSQL 变量替换。变量的 值必须在构造命令字符串时插入其中。
处理动态命令时,你将不得不面对 PL/pgSQL 中单引号的转义问题。请参阅 第 37.2.1 节 中 的概述,它可以为你省些力气。
与 PL/pgSQL 中的所有其他命令不同,由 EXECUTE 语句运行的命令不会在会话生命周期内只预备 和保存一次。相反,该命令在语句每次运行时都会重新预备。命令字符串 可以在函数内动态创建,以对可变的表和列执行操作。
SELECT 命令的结果会被 EXECUTE 丢弃,而且 SELECT INTO 目前不能在 EXECUTE 中使用。有两种方法可以从动态创建的 SELECT 中提取结果:一是使用 第 37.7.4 节 中描述的 FOR-IN-EXECUTE 循环形式,二是使用带 OPEN-FOR-EXECUTE 的游标,如 第 37.8.2 节 中所述。
一个例子:
EXECUTE ''UPDATE tbl SET ''
|| quote_ident(colname)
|| '' = ''
|| quote_literal(newvalue)
|| '' WHERE ...'';
这个例子展示了函数 quote_ident( 和 text)quote_literal( 的使用。 为安全起见,包含列和表标识符的变量应传递给函数 text)quote_ident。包含在构造的命令中应作为字符串字面量 出现的值的变量应传递给 quote_literal。两者都会 采取适当的步骤,分别返回以双引号或单引号括起的输入文本,并正确转义 任何嵌入的特殊字符。
下面是一个大得多的动态命令与 EXECUTE 的例子:
CREATE FUNCTION cs_update_referrer_type_proc() RETURNS integer AS '
DECLARE
referrer_keys RECORD; -- declare a generic record to be used in a FOR
a_output varchar(4000);
BEGIN
a_output := ''CREATE FUNCTION cs_find_referrer_type(varchar, varchar, varchar)
RETURNS varchar AS ''''
DECLARE
v_host ALIAS FOR $1;
v_domain ALIAS FOR $2;
v_url ALIAS FOR $3;
BEGIN '';
-- Notice how we scan through the results of a query in a FOR loop
-- using the FOR <record> construct.
FOR referrer_keys IN SELECT * FROM cs_referrer_keys ORDER BY try_order LOOP
a_output := a_output || '' IF v_'' || referrer_keys.kind || '' LIKE ''''''''''
|| referrer_keys.key_string || '''''''''' THEN RETURN ''''''
|| referrer_keys.referrer_type || ''''''; END IF;'';
END LOOP;
a_output := a_output || '' RETURN NULL; END; '''' LANGUAGE plpgsql;'';
EXECUTE a_output;
END;
' LANGUAGE plpgsql;
有几种方法可以确定命令的效果。第一种方法是使用 GET DIAGNOSTICS 命令,其形式为:
GET DIAGNOSTICSvariable=item[ , ... ] ;
这个命令允许检索系统状态指示器。每个 item 是一个关键字,标识要赋给指定变量的状态值(该变量应具有接收它的正确 数据类型)。当前可用的状态项有 ROW_COUNT(最近发送到 SQL 引擎的 SQL 命令所处理的 行数)和 RESULT_OID(最近一条 SQL 命令插入的最后一行的 OID)。注意 RESULT_OID 只在 INSERT 命令之后有用。
一个例子:
GET DIAGNOSTICS integer_var = ROW_COUNT;
确定命令效果的第二种方法是检查名为 FOUND 的特殊 变量,其类型为 boolean。FOUND 在每个 PL/pgSQL 函数调用开始时为假。它被下列 各类语句设置:
SELECT INTO 语句在返回行时把 FOUND 设为真,未返回行时设为假。
PERFORM 语句在产生(并丢弃)行时把 FOUND 设为真,未产生行时设为假。
UPDATE、INSERT 和 DELETE 语句在至少影响一行时把 FOUND 设为真, 未影响行时设为假。
FETCH 语句在返回行时把 FOUND 设为真,未返回行时设为假。
FOR 语句在迭代一次或多次时把 FOUND 设为真,否则设为假。这适用于 FOR 语句的所有三种变体(整数 FOR 循环、记录集 FOR 循环和动态记录集 FOR 循环)。FOUND 只在 FOR 循环退出时设置:在循环体执行期间, FOUND 不会被 FOR 语句修改, 尽管循环体内其他语句的执行可能会改变它。
FOUND 是一个局部变量;对它的任何更改都只影响当前的 PL/pgSQL 函数。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。