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 文档。
尽管预处理语句的主要目的是避免对语句进行重复的解析分析和规划,但 PostgreSQL 会在使用语句之前强制重新分析和重新规划语句,只要语句中使用的数据库对象经历了定义(DDL)更改或其规划器统计信息自上次使用预处理语句以来已更新。此外,如果 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
语句还使用了一些不同的语法。