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 查询返回零行,NULL 会被赋给目标。如果 SELECT 查询返回多行,第一行会被赋给目标而其余的行被丢弃。(注意, 除非你使用了 ORDER BY,否则“第一行”并没有良好 的定义。)
目前,INTO 子句几乎可以出现在 SELECT 查询的任何位置,但 建议像上面展示的那样把它紧放在 SELECT 关键字之后。 PL/pgSQL的未来版本可能不会这么 宽容地对待 INTO 子句的位置。
有一个名为 FOUND 的boolean类型特殊变量,可以 在 SELECT INTO 之后立即使用它来检查赋值是否成功(也就是 SELECT 是否至少返回了一行)。例如,
SELECT INTO myrec * FROM EMP WHERE empname = myname;
IF NOT FOUND THEN
RAISE EXCEPTION ''employee % not found'', myname;
END IF;
另一种办法是,你可以用 IS NULL(或 ISNULL)条件来测试 RECORD/ROW 结果是否为 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;
它执行一个SELECT query并丢弃结果。 PL/pgSQL变量照常被替换进查询中。
人们可能以为不带 INTO 子句的 SELECT 就能实现这个结果,但 目前唯一被接受的做法是 PERFORM。
一个例子:
PERFORM create_mv(''cs_session_page_requests_mv'',''
SELECT session_id, page_id, count(*) AS n_hits,
sum(dwell_time) AS dwell_time, count(dwell_time) AS dwell_count
FROM cs_fact_table
GROUP BY session_id, page_id '');
你经常会想在PL/pgSQL函数内部生成动态 查询,也就是每次执行时会涉及不同的表或不同数据类型的查询。 PL/pgSQL通常为查询缓存计划的做法在 这类场景下不起作用。为了处理这类问题,提供了 EXECUTE 语句:
EXECUTE query-string;
其中query-string是一个产生字符串 (类型为text)的表达式,该字符串包含要执行的 query。这个字符串被原样送入 SQL 引擎。
特别注意,查询字符串上的PL/pgSQL变量 不会被替换。变量的值必须在构造查询字符串时就被插入其中。
在使用动态查询时,你必须面对PL/pgSQL中单引号 的转义问题。请参阅第 23.11 节中的表格, 那里有能为你节省一些力气的详细解释。
与PL/pgSQL中的所有其他查询不同,由 EXECUTE 语句运行的query不会在服务器的生命期 内只准备和保存一次。相反,query在 语句每次运行时都会被重新准备。 query-string可以在过程内动态创建, 以便对可变的表和字段执行操作。
SELECT 查询的结果会被 EXECUTE 丢弃,而且 EXECUTE 中目前不 支持 SELECT INTO。因此,从动态创建的 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 DIAGNOSTICSvariable=item[ , ... ] ;
这条命令允许检索系统状态指示器。每个 item都是一个关键字,标识要赋给指定 变量的状态值(该变量应当具有接收它的正确数据类型)。当前可用的 状态项有:ROW_COUNT,发送到SQL 引擎的最后一条SQL查询所处理的行数;以及 RESULT_OID,最近的SQL查询插入的 最后一行的 Oid。注意RESULT_OID只在 INSERT 查询 之后有用。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。