↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2
历史版本PostgreSQL 7.4 已于 2010 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

37.6. 基本语句 #

在本节及随后几节中,我们描述 PL/pgSQL 显式 理解的所有语句类型。凡是没有被识别为这些语句类型之一的内容,都被推定为 SQL 命令,并被发送到主数据库引擎执行(在此之后,语句中使用的任何 PL/pgSQL 变量都会被替换)。因此,例如 SQL 命令 INSERT、UPDATE 和 DELETE 可以 被视为 PL/pgSQL 的语句,但这里不专门列出。

37.6.1. 赋值 #

向变量或行/记录字段赋值写作:

identifier := expression;

如上文所述,这种语句中的表达式通过向主数据库引擎发送 SQL SELECT 命令来求值。表达式必须产生单个值。

如果表达式的结果数据类型与变量的数据类型不匹配,或者变量有特定的大小/ 精度(比如 char(20)),结果值将由 PL/pgSQL 解释器使用结果类型的输出函数和 变量类型的输入函数隐式转换。注意,如果结果值的字符串形式不被输入 函数接受,这可能潜在地导致输入函数产生运行时错误。

例子:

user_id := 20;
tax := subtotal * 0.06;

37.6.2. SELECT INTO #

产生多列(但只有一行)的 SELECT 命令的结果可以赋给 一个记录变量、行类型变量或标量变量列表。写法是:

SELECT INTO target select_expressions FROM ...;

其中 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;

37.6.3. 执行无结果的表达式或查询 #

有时人们希望求一个表达式或查询的值但丢弃其结果(通常是因为正在调用 一个有有用副作用但没有有用结果值的函数)。在 PL/pgSQL 中要做到这一点,使用 PERFORM 语句:

PERFORM query;

这会执行 query(它必须是一条 SELECT 语句)并丢弃结果。 PL/pgSQL 变量照常在查询中被替换。此外, 如果查询产生了至少一行,特殊变量 FOUND 会被设置为 真;如果没有产生行,则为假。

注意

人们可能以为不带 INTO 子句的 SELECT 能达到这个效果,但目前唯一被接受的方式是 PERFORM。

一个例子:

PERFORM create_mv(''cs_session_page_requests_mv'', my_query);

37.6.4. 执行动态命令 #

很多时候你会想在 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;

37.6.5. 获取结果状态 #

有几种方法可以确定命令的效果。第一种方法是使用 GET DIAGNOSTICS 命令,其形式为:

GET DIAGNOSTICS variable = 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 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。