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 命令的结果可以赋给 一个记录变量、行类型变量或标量变量列表。写法是:
SELECT INTOtargetexpressionsFROM ...;
其中 target 可以是记录变量、行变量,或由 简单变量和记录/行字段组成的逗号分隔列表。注意,这与 PostgreSQL 对 SELECT INTO 的正常解释大不相同——后者的 INTO 目标是一张新建的表。(如果你想在 PL/pgSQL 函数内部从 SELECT 结果创建表, 请使用语法 CREATE TABLE ... AS SELECT。)
如果用一行或一个变量列表作为目标,选出的值必须与目标的结构完全匹配, 否则会发生运行时错误。当目标是记录变量时,它会自动把自己配置为查询 结果列的行类型。
除了 INTO 子句之外,SELECT 语句与普通 SQL SELECT 查询相同,可以使用其全部能力。
如果 SELECT 查询返回零行,会向目标赋予空值。如果 SELECT 查询返回多行, 第一行被赋给目标,其余的都被丢弃。(注意,除非你使用了 ORDER BY,否则“第一行”没有明确的定义。)
目前,INTO 子句几乎可以出现在 SELECT 查询的任何位置,但建议像上面描述的那样把它紧放在 SELECT 关键字之后。PL/pgSQL 的未来版本可能对 INTO 子句的位置不那么宽容。
你可以在 SELECT INTO 语句之后立即使用 FOUND 来判断赋值是否成功(即 SELECT 语句是否至少返回了一行)。例如:
SELECT INTO myrec * FROM EMP WHERE empname = myname;
IF NOT FOUND THEN
RAISE EXCEPTION ''employee % not found'', myname;
END IF;
要测试记录/行结果是否为空,也可以使用 IS NULL (或 ISNULL)条件。但没有办法知道是否有额外的行被丢弃。
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;
这会执行一条 SELECT query 并丢弃结果。 PL/pgSQL 变量照常在查询中被替换。此外, 如果查询产生了至少一行,特殊变量 FOUND 会被设置为 真;如果没有产生行,则为假。
人们可能以为不带 INTO 子句的 SELECT 能达到这个效果,但目前唯一被接受的方式是 PERFORM。
一个例子:
PERFORM create_mv(''cs_session_page_requests_mv'', my_query);
很多时候你会想在 PL/pgSQL 函数内部生成 动态查询,也就是每次执行时会涉及不同的表或不同数据类型的查询。 PL/pgSQL 为查询缓存计划的常规做法在这种 场景下行不通。为处理这类问题,提供了 EXECUTE 语句:
EXECUTE query-string;
其中 query-string 是一个产生字符串 (类型为 text)的表达式,该字符串包含要执行的 query。这个字符串被按字面送给 SQL 引擎。
特别注意,查询字符串不做 PL/pgSQL 变量替换。变量的 值必须在构造查询字符串时插入其中。
处理动态查询时,你将不得不面对 PL/pgSQL 中单引号的转义问题。请参阅 第 19.11 节 中的表,那里有详细的解释,可以为你省些力气。
与 PL/pgSQL 中的所有其他查询不同,由 EXECUTE 语句运行的 query 不会在服务器生命周期内只预备和保存一次。相反,该 query 在语句每次运行时都会重新预备。 query-string 可以在函数内动态创建,以对 可变的表和字段执行操作。
SELECT 查询的结果会被 EXECUTE 丢弃,而且 SELECT INTO 目前不能在 EXECUTE 中使用。因此, 从动态创建的 SELECT 中提取结果的唯一方法是使用稍后描述的 FOR-IN-EXECUTE 形式。
一个例子:
EXECUTE ''UPDATE tbl SET ''
|| quote_ident(fieldname)
|| '' = ''
|| 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'''';'';
-- This works because we are not substituting any variables
-- Otherwise it would fail. Look at PERFORM for another way to run functions
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 var_integer = 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 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。