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

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 8.1 已于 2010 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

36.6. 基本语句 #

在本节和后续各节中,我们描述PL/pgSQL能够明确理解的所有语句类型。凡是不被识别为这些类型之一的内容,都被认为是 SQL 命令,并在替换其中使用的所有PL/pgSQL变量后被发送到主数据库引擎执行。因此,例如 SQL 命令INSERT、UPDATE和DELETE可以被认为是PL/pgSQL的语句,但这里没有专门列出它们。

36.6.1. 赋值 #

给变量或行/记录字段赋一个值的写法是:

identifier := expression;

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

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

例如:

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

36.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语句与普通 SQLSELECT命令相同,可以使用其全部能力。

INTO子句几乎可以出现在SELECT语句的任何位置。习惯上它要么像上面那样紧接在SELECT之后,要么紧接在FROM之前——也就是select_expressions列表之前或之后。

如果查询返回零行,会把空值赋给目标。如果查询返回多行,第一行会被赋给目标,其余的行将被丢弃。(注意,除非使用了ORDER BY,否则“第一行”并没有明确的定义。)

可以在SELECT INTO语句之后检查特殊的FOUND变量(见第 36.6.6 节),以确定赋值是否成功,即查询是否至少返回了一行。例如:

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

36.6.3. 执行没有结果的表达式或查询 #

有时我们会希望对一个表达式或查询求值但丢弃结果(典型情况是调用一个有有用副作用但没有有用结果值的函数)。要在PL/pgSQL中做到这一点,可以使用PERFORM语句:

PERFORM query;

这会执行query并丢弃结果。编写query的方式与编写 SQLSELECT命令相同,只需把开头的关键字SELECT替换为PERFORM。PL/pgSQL变量会照常被替换到查询中。另外,如果查询至少产生了一行,特殊变量FOUND会被设为真;如果未产生行,则设为假。

注意

我们可能期望不带INTO子句的SELECT能实现这个结果,但当前唯一被接受的方式是PERFORM。

一个示例:

PERFORM create_mv('cs_session_page_requests_mv', my_query);

36.6.4. 什么也不做 #

有时一个什么也不做的占位语句也很有用。例如,它能够指示 if/then/else 链中故意留出的空分支。可以使用NULL语句达到这个目的:

NULL;

例如,下面的两段代码是等价的:

BEGIN
    y := x / 0;
EXCEPTION
    WHEN division_by_zero THEN
        NULL;  -- 忽略错误
END;
BEGIN
    y := x / 0;
EXCEPTION
    WHEN division_by_zero THEN  -- 忽略错误
END;

究竟使用哪一种取决于各人的喜好。

注意

在 Oracle 的 PL/SQL 中,不允许出现空语句列表,并且因此在这种 情况下必须使用 NULL 语句。而 PL/pgSQL 允许你什么也不写。

36.6.5. 执行动态命令 #

很多时候你将想要在PL/pgSQL函数中产生动态命令,也就是每次执行中会涉及到不同表或不同数据类型的命令。PL/pgSQL通常对命令缓存计划的尝试在这种情境下无法工作。要处理这一类问题,提供了EXECUTE语句:

EXECUTE command-string [ INTO target ];

其中command-string是一个表达式,其求值结果(类型为 text)是包含待执行命令的字符串,而target是一个记录变量、一个行变量或者一个逗号分隔的简单变量以及记录/行字段的列表。

特别要注意,在计算得到的命令字符串上不会做PL/pgSQL变量的替换。变量的值必须在构造命令字符串时被插入其中。

与PL/pgSQL中的所有其他命令不同,通过EXECUTE语句运行的命令不会在会话生命周期内只准备和保存一次。相反,每次运行该语句时都会重新准备命令。命令字符串可以在函数内动态创建,以对不同的表和列执行操作。

INTO子句指定一个SELECT命令的结果应该被赋值到哪里。如果提供了一个行变量或变量列表,它必须完全匹配该SELECT命令结果的结构(当一个记录变量被提供时,它会自动把它自己配置为匹配结果结构)。如果返回多个行,只有第一个行会被赋值给INTO变量。如果没有返回行,NULL 会被赋值给INTO变量。如果没有指定INTO子句,SELECT命令的结果将被丢弃。

目前不支持在EXECUTE中使用SELECT INTO。

在使用动态命令时,经常需要处理单引号的转义。我们推荐在函数体中使用美元引用来引用固定文本。(如果你有未使用美元引用的旧代码,请参阅第 36.2.1 节中的概述;在把这类代码转换成更合理的写法时,它会帮你省下一些工夫。)

将被插入到构造出的查询中的动态值需要被小心地处理,因为它们 自身可能包含引号字符。一个示例(这假设你对函数整体使用了美元 引用,因此引号标记不需要被双写):

EXECUTE 'UPDATE tbl SET '
        || quote_ident(colname)
        || ' = '
        || quote_literal(newvalue)
        || ' WHERE key = '
        || quote_literal(keyvalue);

这个示例展示了quote_ident和quote_literal函数的用法。为了安全起见,包含列和表标识符的表达式应被传给quote_ident。包含在构造出的命令中应作为字符串字面量出现的值的表达式,应被传给quote_literal。这两个函数都会采取适当措施,分别返回用双引号或单引号括起来的输入文本,并正确转义其中嵌入的特殊字符。

请注意美元符号引用只对引用固定文本有用。把上面的示例写成下面这样将是一个非常糟糕的主意:

EXECUTE 'UPDATE tbl SET '
        || quote_ident(colname)
        || ' = $$'
        || newvalue
        || '$$ WHERE key = '
        || quote_literal(keyvalue);

因为如果newvalue的内容碰巧含有$$,那么这段代码就会出问题。同样的问题也适用于你选择的任何其他美元符号引用定界符。因此,要想安全地引用事先不知道的文本,必须使用quote_literal。

动态命令和EXECUTE的一个更大的示例可以在例 36.6中找到,它会构建并且执行一个CREATE FUNCTION命令来定义一个新的函数。

36.6.6. 获取结果状态 #

有好几种方法可以判断一条命令的效果。第一种方法是使用 GET DIAGNOSTICS 命令,其形式如下:

GET DIAGNOSTICS variable = item [ , ... ];

这条命令允许检索系统状态指示符。每个 item 是一个关键字,它标识一个要赋给指定变量(该变量应具有正确的数据类型来接收它)的状态值。 当前可用的状态项是 ROW_COUNT(发送到 SQL 引擎的最后一条 SQL 命令所处理的行数)和 RESULT_OID(最近一条 SQL 命令插入的最后一行的 OID)。注意, RESULT_OID 只有在向包含 OID 的表执行 INSERT 命令之后才有用。

一个示例:

GET DIAGNOSTICS integer_var = ROW_COUNT;

第二种确定命令效果的方法是检查名为 FOUND 的特殊变量,其类型为 boolean。在每次 PL/pgSQL 函数调用开始时, FOUND 都为假。以下各类语句会设置它:

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