↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 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.3 已结束支持。 请参阅 当前版本手册.

19.5. 基本语句 #

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

19.5.1. 赋值 #

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

identifier := expression;

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

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

例子:

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

19.5.2. SELECT INTO #

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

SELECT INTO target expressions FROM ...;

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

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

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

PERFORM query;

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

注意

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

一个例子:

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

19.5.4. 执行动态查询 #

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

19.5.5. 获取结果状态 #

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

GET DIAGNOSTICS variable = 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 函数。

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.