2025年9月25日: PostgreSQL 18 发布!
支持版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持版本: 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

PREPARE

PREPARE — 准备一个用于执行的语句

概要

PREPARE name [ ( data_type [, ...] ) ] AS statement

描述

PREPARE 创建一个预备语句。预备语句是一个服务器端对象,可以用来优化性能。当执行 PREPARE 语句时,指定的语句会被解析、分析和重写。当随后发出 EXECUTE 命令时,预备语句会被规划和执行。这种分工可以避免重复的解析和分析工作,同时允许执行计划依赖于提供的具体参数值。

预备语句可以接受参数:在执行时替换到语句中的值。创建预备语句时,使用 $1, $2 等按位置引用参数。还可以选择性地指定一组相应的参数数据类型。当参数的数据类型未指定或声明为 unknown 时,该类型将从参数首次被引用的上下文(如果可能)中推断出来。执行语句时,在 EXECUTE 语句中指定这些参数的实际值。有关更多信息,请参阅 EXECUTE

预备语句只在当前数据库会话期间有效。会话结束后,预备语句就会被忘记,因此在再次使用之前必须重新创建。这也意味着单个预备语句不能被多个并发的数据库客户端使用;然而,每个客户端都可以创建自己的预备语句来使用。可以使用 DEALLOCATE 命令手动清理预备语句。

当一个会话被用于执行大量相似的语句时,预备语句可能具有最大的性能优势。性能差异尤其显著,如果语句的规划或重写很复杂,例如,如果查询涉及连接多个表或需要应用多个规则。如果语句的规划和重写相对简单,但执行成本相对较高,那么预备语句的性能优势将不太明显。

参数

name

为这个特定的预备语句指定的任意名称。它在一个会话中必须是唯一的,并且之后用于执行或解除先前预备语句的准备。

data_type

预备语句参数的数据类型。如果某个参数的数据类型未指定或被指定为 unknown,它将从参数首次被引用的上下文中推断出来。要在预备语句本身中引用参数,请使用 $1, $2 等。

statement

任何 SELECTINSERTUPDATEDELETEMERGEVALUES 语句。

注释

可以使用 通用计划自定义计划 来执行预备语句。通用计划在所有执行中都相同,而自定义计划是为使用该调用中提供的参数值的特定执行生成的。使用通用计划可以避免规划开销,但在某些情况下,自定义计划在执行时会更有效,因为规划器可以利用参数值的知识。(当然,如果预备语句没有参数,那么这就不重要了,并且总是使用通用计划。)

默认情况下(即,当 plan_cache_mode 设置为 auto 时),服务器将自动选择是为带有参数的预备语句使用通用计划还是自定义计划。当前的规则是,前五次执行都使用自定义计划,并计算这些计划的平均估计成本。然后创建一个通用计划,并将其估计成本与平均自定义计划成本进行比较。后续执行使用通用计划,前提是其成本不会比平均自定义计划成本高到使得重复重新规划看起来更可取。

可以通过将 plan_cache_mode 设置为 force_generic_planforce_custom_plan 来覆盖此启发式方法,强制服务器使用通用计划或自定义计划。此设置主要在通用计划的成本估算因某种原因而严重失准时有用,允许选择它,即使其实际成本远高于自定义计划。

要检查 PostgreSQL 正在为预备语句使用的查询计划,请使用 EXPLAIN,例如:

EXPLAIN EXECUTE name(parameter_values);

如果正在使用通用计划,它将包含参数符号 $n,而自定义计划将在其中包含提供的参数值。

有关查询规划以及 PostgreSQL 为此目的收集的统计信息的更多信息,请参阅 ANALYZE 文档。

尽管预备语句的主要目的是避免重复解析和规划语句,但只要语句中使用的数据库对象自上次使用预备语句以来经历了定义(DDL)更改或其规划器统计信息已更新,PostgreSQL 就会强制在重新使用语句之前进行重新分析和重新规划。此外,如果 search_path 的值在使用之间发生变化,语句将使用新的 search_path 进行重新解析。(此后者的行为是 PostgreSQL 9.3 起新增的。)这些规则使得使用预备语句在语义上几乎等同于一次又一次地提交相同的查询文本,但在没有对象定义更改的情况下具有性能优势,特别是当最佳计划在多次使用中保持不变时。一个语义等价不完美的例子是,如果语句使用非限定名称引用一个表,然后在一个出现在 search_path 中更早的模式中创建了一个同名的新表,则不会发生自动重新解析,因为语句中使用的对象没有改变。但是,如果其他更改强制重新解析,则后续使用将引用新表。

您可以通过查询 pg_prepared_statements 系统视图来查看会话中所有可用的预备语句。

示例

INSERT 语句创建一个预备语句,然后执行它

PREPARE fooplan (int, text, bool, numeric) AS
    INSERT INTO foo VALUES($1, $2, $3, $4);
EXECUTE fooplan(1, 'Hunter Valley', 't', 200.00);

SELECT 语句创建一个预备语句,然后执行它

PREPARE usrrptplan (int) AS
    SELECT * FROM users u, logs l WHERE u.usrid=$1 AND u.usrid=l.usrid
    AND l.date = $2;
EXECUTE usrrptplan(1, current_date);

在此示例中,第二个参数的数据类型未指定,因此它从 $2 使用的上下文中推断出来。

兼容性

SQL 标准包含一个 PREPARE 语句,但它仅用于嵌入式 SQL。此版本的 PREPARE 语句也使用一种稍有不同的语法。

另请参阅

DEALLOCATE, EXECUTE

提交更正

如果您在文档中看到任何不正确的内容、与您在使用该特定功能时的体验不符或需要进一步澄清的地方,请使用 此表单 报告文档问题。