PREPARE — 准备一个语句以供执行
PREPAREname
[ (data_type
[, ...] ) ] ASstatement
PREPARE
创建一个预处理语句。预处理语句是一个服务器端对象,可用于优化性能。当执行 PREPARE
语句时,将解析、分析和重写指定的语句。当随后发出 EXECUTE
命令时,将规划并执行预处理语句。这种分工避免了重复的解析分析工作,同时允许执行计划取决于提供的特定参数值。
预处理语句可以接受参数:在执行时替换到语句中的值。创建预处理语句时,使用 $1
、$2
等按位置引用参数。可以选择指定相应的参数数据类型列表。当未指定参数的数据类型或声明为 unknown
时,将从首次引用参数的上下文(如果可能)推断类型。执行语句时,在 EXECUTE
语句中指定这些参数的实际值。有关详细信息,请参阅 EXECUTE。
预处理语句仅在当前数据库会话期间有效。当会话结束时,预处理语句将被遗忘,因此必须在再次使用之前重新创建它。这也意味着单个预处理语句不能被多个同时运行的数据库客户端使用;但是,每个客户端都可以创建自己的预处理语句来使用。可以使用 DEALLOCATE
命令手动清理预处理语句。
当单个会话用于执行大量相似语句时,预处理语句可能具有最大的性能优势。如果语句的规划或重写很复杂,例如,如果查询涉及多个表的连接或需要应用多个规则,则性能差异将特别显著。如果语句的规划和重写相对简单,但执行成本相对较高,则预处理语句的性能优势将不太明显。
name
为此特定的预处理语句指定的任意名称。它在一个会话中必须是唯一的,并随后用于执行或取消分配先前准备的语句。
data_type
预处理语句的参数的数据类型。如果未指定特定参数的数据类型或将其指定为 unknown
,则将从首次引用参数的上下文中推断出该数据类型。要在预处理语句本身中引用参数,请使用 $1
、$2
等。
statement
任何 SELECT
、INSERT
、UPDATE
、DELETE
、MERGE
或 VALUES
语句。
可以使用通用计划或自定义计划执行预处理语句。通用计划在所有执行中都是相同的,而自定义计划是针对使用该调用中给定的参数值的特定执行而生成的。使用通用计划避免了计划开销,但在某些情况下,自定义计划会更有效地执行,因为规划器可以利用参数值的知识。(当然,如果预处理语句没有参数,那么这是没有意义的,并且始终使用通用计划。)
默认情况下(即当 plan_cache_mode 设置为 auto
时),服务器将自动选择是否对具有参数的预处理语句使用通用计划或自定义计划。当前规则是,前五个执行使用自定义计划,并计算这些计划的平均估计成本。然后创建一个通用计划,并将其估计成本与平均自定义计划成本进行比较。如果通用计划的成本没有比平均自定义计划成本高出太多,以至于重复重新计划似乎更可取,则后续执行使用通用计划。
可以通过将 plan_cache_mode
分别设置为 force_generic_plan
或 force_custom_plan
来覆盖此启发式方法,从而强制服务器使用通用计划或自定义计划。如果由于某种原因通用计划的成本估算严重偏离,则此设置主要有用,即使其实际成本远高于自定义计划,也可以选择它。
要检查 PostgreSQL 用于预处理语句的查询计划,请使用 EXPLAIN
,例如
EXPLAIN EXECUTEname
(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
语句也使用略有不同的语法。
如果您在文档中发现任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用此表单报告文档问题。