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 语句的这个版本所使用的语法也略有不同。