pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
目录
PL/pgSQL 是一种用于 PostgreSQL 数据库系统的可载入过程语言。PL/pgSQL 的设计目标,是创建一种具备以下特性的可载入过程语言:
可用于创建函数和触发器函数,
为SQL语言增加控制结构,
可以执行复杂计算,
继承所有用户定义的类型、函数和操作符,
可定义为受服务器信任的语言,
便于使用。
除了用户定义类型的输入/输出转换和计算函数之外,凡是能用 C 语言函数定义的东西,也都可以用 PL/pgSQL 完成。例如,可以创建复杂的条件计算函数,随后用它们定义操作符,或者把它们用于索引表达式。
PL/pgSQL 调用处理器会在函数第一次被调用时(在每个会话内)解析函数的源文本,并生成一棵内部二进制指令树。这棵指令树完整地翻译了 PL/pgSQL 的语句结构,但函数中使用的各个 SQL 表达式和 SQL 命令并不会被立即翻译。
当每个表达式和 SQL 命令在函数中第一次被使用时,PL/pgSQL 解释器会创建一个预备执行计划(使用 SPI 管理器的 SPI_prepare 和 SPI_saveplan 函数)。之后再遇到该表达式或命令时,将复用这个预备计划。因此,一个包含大量条件代码、可能需要为许多语句生成执行计划的函数,只会准备和保存数据库连接生命周期内真正用到的那些计划。这可以显著减少为 PL/pgSQL 函数中的语句解析并生成执行计划所需的总时间。一个缺点是,特定表达式或命令中的错误,可能要到执行到函数的那一部分时才会被发现。
一旦 PL/pgSQL 为函数中的某条命令生成了执行计划,它就会在数据库连接的整个生命周期内复用该计划。通常这有利于性能,但如果你动态地修改数据库模式,就可能带来一些问题。例如:
CREATE FUNCTION populate() RETURNS integer AS $$
DECLARE
-- declarations
BEGIN
PERFORM my_function();
END;
$$ LANGUAGE plpgsql;
如果你执行上面的函数,它会为 PERFORM 语句生成的执行计划引用 my_function() 的 OID。此后,如果你删除并重新创建 my_function(),那么 populate() 将再也找不到 my_function()。你将不得不重新创建 populate(),或者至少启动一个新的数据库会话,使其重新被编译。避免这一问题的另一种方法是,在更新 my_function() 的定义时使用 CREATE OR REPLACE FUNCTION(函数被“替换”时,其 OID 不会改变)。
由于 PL/pgSQL 以这种方式保存执行计划,直接出现在 PL/pgSQL 函数中的 SQL 命令在每次执行时必须引用相同的表和列;也就是说,不能把参数用作 SQL 命令中表或列的名称。要绕开这一限制,可以使用 PL/pgSQL 的 EXECUTE 语句构造动态命令——代价是每次执行都要构造一个新的执行计划。
PL/pgSQL 的 EXECUTE 语句与 PostgreSQL 服务器支持的 EXECUTE SQL 语句无关。服务器的 EXECUTE 语句不能用在 PL/pgSQL 函数中(也用不着它)。
SQL 是 PostgreSQL 以及大多数其他关系数据库使用的查询语言。它具有可移植性,也容易学习。但每条 SQL 语句都必须由数据库服务器单独执行。
这意味着客户端应用必须把每个查询发送给数据库服务器,等待其处理完成,接收并处理结果,做一些计算,然后再向服务器发送更多查询。这一切都会产生进程间通信开销;如果客户端与数据库服务器不在同一台机器上,还会额外产生网络开销。
借助 PL/pgSQL,你可以把一段计算逻辑和一系列查询放在数据库服务器内部执行。这样既具备了过程语言的能力,也保留了 SQL 的易用性,同时还能显著减少客户端/服务器通信开销。
消除了客户端和服务器之间额外的往返通信
客户端不需要的中间结果不必在服务器与客户端之间编组或传输
可以避免多轮查询解析
与不使用存储函数的应用相比,这可能带来相当可观的性能提升。
此外,通过 PL/pgSQL 你可以使用 SQL 的全部数据类型、操作符和函数。
用 PL/pgSQL 编写的函数,可以接受服务器支持的任意标量或数组数据类型作为参数,也可以返回这些类型中的任意一种。它们还可以接受或返回任何按名称指定的复合类型(行类型)。也可以把 PL/pgSQL 函数声明为返回 record,这表示结果是一种行类型,其列由调用查询中的指定内容决定,如 第 7.2.1.4 节 所述。
PL/pgSQL 函数也可以声明为接受和返回多态类型 anyelement 和 anyarray。多态函数处理的 实际数据类型可以在不同调用之间变化,如 第 32.2.5 节 所述。示例见 第 36.4.1 节。
PL/pgSQL 函数还可以声明为返回它能够返回单个实例的任意数据类型的“集合”(或表)。这类函数通过对结果集中每个需要的元素执行 RETURN NEXT 来生成输出。
最后,如果某个 PL/pgSQL 函数没有有意义的返回值,可以将其声明为返回 void。
PL/pgSQL目前还没有对域类型的完整支持:它把域当作底层标量类型同样对待。这意味着与域关联的约束不会被强制执行。这对函数参数来说不是问题,但如果你把一个PL/pgSQL函数声明为返回域类型,就存在隐患。
PL/pgSQL 函数也可以声明为使用输出参数,而不是显式指定返回类型。这并没有为该语言增加新的基本能力,但通常会更方便,尤其是在需要返回多个值时。
具体示例见 第 36.4.1 节 和 第 36.7.1 节。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。