关于 YMatrix
标准集群部署
数据写入
数据迁移
数据查询
运维监控
参考指南
工具指南
数据类型
存储引擎
执行引擎
流计算引擎
灾难恢复
系统配置参数
索引
扩展
SQL 参考
常见问题(FAQ)
准备一条待执行的语句。
PREPARE <name> [ (<data_type> [, ...] ) ] AS <statement>
PREPARE 创建预编译语句。预编译语句是一种服务器端对象,可用于优化性能。当执行 PREPARE 语句时,指定的语句将被解析、分析并重写。随后当发出 EXECUTE 命令时,预编译语句将被规划并执行。这种分工避免了重复的解析分析工作,同时允许执行计划依赖于提供的具体参数值。
预编译语句可包含参数,这些参数将在语句执行时被替换为实际值。创建预编译语句时,需通过位置引用参数(如$1、$2等),并可选地指定对应的参数数据类型列表。若参数数据类型未指定或声明为未知,则系统将根据参数首次被引用的上下文推断类型(若可行)。执行语句时,需在EXECUTE语句中为这些参数指定实际值。
预编译语句仅在当前数据库会话期间有效。当会话结束时,预编译语句将被清除,因此必须重新创建才能再次使用。这也意味着单个预编译语句无法被多个同时连接的数据库客户端共享;不过每个客户端均可创建自己的预编译语句使用。可通过DEALLOCATE命令手动清理预编译语句。
预编译语句在单个会话中执行大量相似语句时具有最大的性能优势。若语句的执行计划或重写过程较为复杂(例如涉及多表连接或需应用多条规则的查询),性能差异将尤为显著。若语句的执行计划和重写相对简单,但运行成本较高,则预编译语句的性能优势将不那么明显。
预编译语句可使用通用执行计划或自定义执行计划运行。通用执行计划在所有执行过程中保持不变,而自定义执行计划则根据调用时提供的参数值为特定执行生成。使用通用执行计划可避免计划开销,但在某些情况下,自定义执行计划能显著提升运行效率,因为计划器可利用参数值的相关知识。若预编译语句不含参数,则始终采用通用执行计划。
默认情况下(服务器配置参数 plan_cache_mode 的默认值为 auto),服务器会自动决定对带参数的预编译语句使用通用计划还是自定义计划。当前规则是:前五次执行使用自定义计划,随后数据库会计算这些计划的平均预估成本。随后生成通用执行计划,并将该计划的预估成本与平均自定义计划成本进行比较。后续执行将采用通用计划,除非其成本显著高于平均自定义计划成本,以致重新规划更具优势。
您可以通过将 plan_cache_mode 分别设置为 force_generic_plan 或 force_custom_plan 来覆盖此启发式规则,强制服务器使用通用计划或自定义计划。此设置主要适用于通用计划的成本估算因某些原因严重失准的情况,即使其实际成本远高于自定义计划,仍会选择通用计划。
要检查数据库为预编译语句使用的查询计划,请使用EXPLAIN命令,例如:
EXPLAIN EXECUTE <name>(<parameter_values>);
若使用通用计划,其中将包含参数符号 $n;而自定义计划则会将提供的参数值替换进去。
有关查询计划制定及数据库为此收集的统计信息的更多详情,请参阅ANALYZE文档。
尽管预编译语句的主要目的是避免重复解析和规划语句,但当语句中使用的数据库对象自上次使用预编译语句以来经历了定义性(DDL)变更时,系统将强制在使用前重新解析和规划该语句。此外,若每次使用时search_path的值发生变化,语句将使用新的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 语句的预编译语句,然后执行它。请注意,第二个参数的数据类型未指定,因此根据 $2 的使用上下文进行推断:
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);
SQL标准包含PREPARE语句,但该语句仅限于嵌入式SQL中使用,且采用不同的语法。