|
23.5. 基本语句本节以及随后的一节里,我们描述所有 PL/pgSQL 明确可以理解的语句类型.任何无法识别为这样类型的语句将被做为 SQL 查询看待,并且被发送到主数据库引擎执行(在将语句中用到的任何 PL/pgSQL 变量进行替换之后).因此, 举例来说,SQL INSERT , UPDATE ,和 DELETE 命令可能被认为是 PL/pgSQL 语句,但是它们并未在此明确列出. 23.5.1. 赋值给一个变量或行/记录赋值用下面方法∶ identIFier := expression ; 如上所述,这样的语句中的表达式是用一个发送到主数据库引擎的 SQL SELECT 命令计算的.该表达式必须生成单一的数值. 如果表达式的结果数据类型和变量数据类型不一致, 或者变量具有已知的尺寸/精度(例如 char(20) ), 结果值将隐含地被 PL/pgSQL 解释器用结果类型的输出函数 和变量类型的输入函数转换. 要注意的是,如果结果数值的字串形式不是输入函数可以接受的形式, 那么这样做可能导致类型输入函数产生的运行时错误. 例子∶ user_id := 20; tax := subtotal * 0.06;
23.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 查询返回零行,则给目标赋予 NULL. 如果 SELECT 查询返回多行,那么将第一行赋予目标并抛弃其它的行. (请注意∶除非你用了 ORDER BY,否则 "第一行" 是不明确的.) 目前,INTO 子句可以出现在 SELECT 查询里的几乎任何地方, 但我们建议把它放在 SELECT 紧后面,正如我们上面描写的那样. 将来的 PL/pgSQL 版本可能不会对 INTO 子句的位置再那么宽松了. PL/pgSQL 里有一个特殊的变量,叫 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 -- 用户没有输入主页,返回"http://" RETURN ''http://''; END IF; END;
23.5.3. 执行一个没有结果的表达式或者查询有时候我们希望计算一个表达式或者一个查询,但是却丢弃其结果 (通常因为我们经常调用一些存在有用的副作用但是不存在有用结果值的 函数).要在 PL/pgSQL 里干这件事, 你可以使用 PERFORM 语句∶ PERFORM query ; 这条语句执行一个 SELECT query 并且丢弃结果. PL/pgSQL 的变量和平常一样 代换到查询中.
一个例子∶ 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 '');
23.5.4. 执行动态查询你经常会希望在你的 PL/pgSQL 函数里生成动态查询. 也就是那些每次执行的时候都会涉及不同表或不通数据类型的查询. 在这样的情况下, PL/pgSQL 试图为查询 缓冲查询规划的企图将不再合适.为了处理这样的问题,我们提供了 EXECUTE 语句∶ EXECUTE query-string ; 这里的 query-string 是一个生成字串(类型为 text )的表达式,该字串包含要执行的 查询(query) . 该字串的文本将被传递给 SQL 引擎. 请特别注意在该查询字串里将不会发生任何 PL/pgSQL 变量代换.变量的数值必需在构造查询字串的时候插入该字串. 在使用动态查询的时候,你就必须面对 PL/pgSQL 的单引号 逃逸的问题.请参考 Section 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; -- 声明一个用于 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 ''; -- -- 请注意这里是如何在一个 FOR 循环中扫描所有的结果的 -- 这里用的是 FOR <record> 构造 -- 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'''';''; -- 我们可以象下面这样写是因为我们没有替换任何变量 -- 否则它会出错.看看 PERFORM 获取另外一种运行函数的方法 EXECUTE a_output; END; ' LANGUAGE 'plpgsql';
23.5.5. 获取结果状态
GET DIAGNOSTICS
variable
=
item
[
, ...
] ;
这条命令允许我们检索系统状态标识符.每个 item 是一个关键字,表示一个将要赋予该特定变量的状态值(该变量应该 和要接收的数值类型相同).当前可用的状态项有 ROW_COUNT , 最后一个发送给 SQL 引擎的 SQL 查询处理的行的数量;和 RESULT_OID ,最后一条 SQL 查询插入的最后一行的 Oid.请注意 RESULT_OID 只有在一个 INSERT 查询之后才有用. |