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
語句也使用一種稍有不同的語法。
如果您在文件中看到任何不正確的內容、與您在使用該特定功能時的體驗不符或需要進一步澄清的地方,請使用 此表單 報告文件問題。