Chapter 37. PL/pgSQL - SQL 過程語言

Table of Contents
37.1. 概述
37.1.1. 使用PL/pgSQL的優點
37.1.2. 所支持的參數和結果數據類型
37.2. 開發 PL/pgSQL 的一些提示
37.3. PL/pgSQL的結構
37.4. 聲明
37.4.1. 函數參數的別名
37.4.2. 拷貝類型
37.4.3. 行類型
37.4.4. 記錄類型
37.4.5. RENAME
37.5. 表達式
37.6. 基本語句
37.6.1. 賦值
37.6.2. SELECT INTO
37.6.3. 執行一個沒有結果的表達式或者命令
37.6.4. 執行動態命令
37.6.5. 獲取結果狀態
37.7. 控制結構
37.7.1. 從函數返回
37.7.2. 條件
37.7.3. 簡單循環
37.7.4. 遍歷命令結果
37.8. 遊標
37.8.1. 聲明遊標變量
37.8.2. 打開遊標
37.8.3. 使用遊標
37.9. 錯誤和消息
37.10. 觸發器過程
37.11. Oracle的 PL/SQL 移植
37.11.1. 移植樣例
37.11.2. 其它要注意的東西
37.11.3. 附錄

PL/pgSQLPostgreSQL 數據庫系統的一個可裝載的過程語言。 PL/pgSQL的設計目標是創建一種可裝載的過程語言,可以

37.1. 概述

PL/pgSQL 函數第一次(在任何一個服務器進程內部)被調用時, PL/pgSQL 的調用句柄分析函數源文本生成二進制指令樹。 該指令樹完全轉換了 PL/pgSQL 語句結構, 但是在函數內使用到的獨立的 SQL 表達式和 SQL 命令並未立即轉換。

在每個函數中用到的表達式和 SQL 命令在函數裡首次使用的時候, PL/pgSQL 解釋器創建一個準備好的執行規劃(使用 SPI 管理器的 SPI_prepareSPI_saveplan 函數)。 隨後對該表達式或者命令的訪問都將使用已準備好的規劃。 因此,一個在條件代碼中有許多語句,可能需要執行規劃的函數, 只需要準備和保存那些真正在數據庫聯接期間真正使用到的規劃。 這樣可以有效地減少為 PL/pgSQL 函數裡的語句生成分析和執行規劃的總時間。 不過有個缺點是在特定表達式或者命令中的錯誤可能要到函數中的那部分執行到的時候才能發現。

一旦 PL/pgSQL 在函數裡為一個命令制定了執行計劃, 那麼它將在該次數據庫聯接的生命期內復用該規劃。 這麼做在性能上通常會更好一些,但是如果你動態地修改你的數據庫模式,那麼就可能有問題。 比如:

CREATE FUNCTION populate() RETURNS integer AS '
DECLARE
    -- 聲明段
BEGIN
    PERFORM my_function();
END;
' LANGUAGE plpgsql;

如果你執行上面的函數,那麼它將在為PERFORM語句生成的執行計劃中中引用 my_function() 的 OID。 然後,如果你刪除然後重新創建 my_function(), 那麼 populate() 就會再也找不到 my_function()。 這時候你只能重新創建 populate(), 或者至少是重新開始一個新的數據庫會話,好讓該函數能重新編譯一次。 另外一個避免這種問題的方法是在更新my_function 的定義的時候 使用 CREATE OR REPLACE FUNCTION (如果一個函數被"替換",那麼它的 OID 將不會變化)。

因為Pl/pgSQL用這種方法保存執行規劃, 所以那些在PL/pgSQL裡直接出現的 SQL 命令必須在每次執行的時候引用相同的表和字段; 也就是說,你不能拿一個參數用做 SQL 命令中的表或者字段的名稱。 要繞開這個限制,你可以用 PL/pgSQLEXECUTE語句動態地構造命令 --- 代價是每次執行的時候都構造一個新的命令計劃。

注意: PL/pgSQLEXECUTE語句和 PostgreSQL 服務器支持的EXECUTE語句沒有關系。 服務器的EXECUTE語句不能在 PL/pgSQL 函數中使用(而且也沒必要)。

除了用于用戶定義類型的輸入/輸出轉換和計算函數以外, 任何可以在 C 語言函數裡定義的東西都可以在 PL/pgSQL裡使用。 比如,我們可以創建復雜的條件計算函數, 並隨後將之用于定義操作符或者用于函數索引中。

37.1.1. 使用PL/pgSQL的優點

SQLPostgreSQL (和大多數其它關系型數據庫)用做命令語言的語言。 它是可以移植的,並且容易學習使用。 但是所有 SQL 語句都必須由數據庫服務器獨立地執行。

這就意味著你的客戶端應用必須把每條命令發送到數據庫服務器, 等待它處理這個命令,接收結果,做一些運算,然後給服務器發送另外一條命令。 所有這些東西都會產生進程間通訊,並且如果你的客戶端在另外一台機器上甚至還會導致網絡開銷。

如果使用了PL/pgSQL,那麼你可以把一塊運算和一系列命令在數據庫服務器裡面組成一個塊, 這樣就擁有了過程語言的力量並且簡化 SQL 的使用,因而節約了大量的時間,因為你用不著付出客戶端/服務器通訊的過熱。 這樣可能產生明顯的性能提升。

同樣,在 PL/pgSQL 裡,你可以使用 SQL 的所有數據類型,操作符和函數。

37.1.2. 所支持的參數和結果數據類型

並且它們可以返回這種任何這種類型的數值。它們還可以接受或者返回任意用名字聲明的復合類型(行類型)。 我們還可以聲明一個 PL/pgSQL 函數為返回 record 的函數, 意思是結果是一個行類型,這個行的字段是在調用它的查詢中指定的,就像我們在 Section 7.2.1.4 裡討論的那樣。

PL/pgSQL 函數還可以聲明為接受並返回多態的類型 anyelementanyarray。 一個多態的函數實際操作的數據類型可以在不同的調用環境中變化, 如我們在 Section 33.2.5 裡討論的那樣。 一個例子是 Section 37.4.1

PL/pgSQL 還可以聲明為返回一個它們可以返回的任何單個實例的"集(set)",或者表。 這樣的函數通過為結果集每個需要返回的元素執行一個 RETURN NEXT 生成它的輸出。

最後,PL/pgSQL 函數可以聲明為返回 void,如果它沒啥有用的東西可以返回的話。