2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本: devel
不支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2

41.3. 宣告 #

塊中使用的所有變數都必須在塊的宣告部分進行宣告。(唯一的例外是:迭代整數值範圍的 FOR 迴圈的迴圈變數會被自動宣告為整型變數,同樣,迭代遊標結果的 FOR 迴圈的迴圈變數會被自動宣告為記錄變數。)

PL/pgSQL 變數可以是任何 SQL 資料型別,例如 integervarcharchar

下面是一些變數宣告的例子

user_id integer;
quantity numeric(5);
url varchar;
myrow tablename%ROWTYPE;
myfield tablename.columnname%TYPE;
arow RECORD;

變數宣告的通用語法是

name [ CONSTANT ] type [ COLLATE collation_name ] [ NOT NULL ] [ { DEFAULT | := | = } expression ];

DEFAULT 子句(如果給出)指定了在進入塊時分配給變數的初始值。如果未給出 DEFAULT 子句,則變數被初始化為SQL空值。 CONSTANT 選項可以防止變數在初始化後被賦值,因此其值在塊的持續時間內將保持不變。 COLLATE 選項指定了用於變數的排序規則(請參閱 第 41.3.6 節)。如果指定了 NOT NULL,則將空值賦給它會導致執行時錯誤。所有宣告為 NOT NULL 的變數都必須指定一個非空預設值。等號 (=) 可以代替符合 PL/SQL 標準的 :=

變數的預設值會在每次進入塊時進行計算並賦給變數(而不僅僅是每次函式呼叫時一次)。因此,例如,將 now() 賦值給一個 timestamp 型別的變數會導致該變數具有當前函式呼叫時的時間,而不是函式預編譯時的時間。

示例

quantity integer DEFAULT 32;
url varchar := 'http://mysite.com';
transaction_time CONSTANT timestamp with time zone := now();

聲明後,變數的值可以在同一塊的後續初始化表示式中使用,例如

DECLARE
  x integer := 1;
  y integer := x + 1;

41.3.1. 宣告函式引數 #

傳遞給函式的引數使用識別符號 $1$2 等命名。可選地,可以為 $n 引數名稱宣告別名以提高可讀性。然後可以使用別名或數字識別符號來引用引數值。

有兩種方法可以建立別名。首選方法是在 CREATE FUNCTION 命令中為引數命名,例如

CREATE FUNCTION sales_tax(subtotal real) RETURNS real AS $$
BEGIN
    RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

另一種方法是顯式宣告一個別名,使用宣告語法

name ALIAS FOR $n;

此樣式的相同示例看起來像

CREATE FUNCTION sales_tax(real) RETURNS real AS $$
DECLARE
    subtotal ALIAS FOR $1;
BEGIN
    RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

注意

這兩個示例並不完全等價。在第一種情況下,subtotal 可以被引用為 sales_tax.subtotal,但在第二種情況下則不能。(如果我們給內部塊添加了一個標籤,subtotal 就可以用該標籤限定,而不是。)

更多示例

CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$
DECLARE
    v_string ALIAS FOR $1;
    index ALIAS FOR $2;
BEGIN
    -- some computations using v_string and index here
END;
$$ LANGUAGE plpgsql;


CREATE FUNCTION concat_selected_fields(in_t sometablename) RETURNS text AS $$
BEGIN
    RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
$$ LANGUAGE plpgsql;

PL/pgSQL 函式聲明瞭輸出引數時,輸出引數會以與普通輸入引數相同的方式獲得 $n 名稱和可選別名。輸出引數實際上是一個初始值為 NULL 的變數;它應該在函式執行期間被賦值。引數的最終值就是返回的值。例如,銷售稅示例也可以這樣完成

CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$
BEGIN
    tax := subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

請注意,我們省略了 RETURNS real — 我們可以包含它,但它是多餘的。

要呼叫帶有 OUT 引數的函式,請在函式呼叫中省略輸出引數。

SELECT sales_tax(100.00);

輸出引數在返回多個值時最有用。一個簡單的例子是

CREATE FUNCTION sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$
BEGIN
    sum := x + y;
    prod := x * y;
END;
$$ LANGUAGE plpgsql;

SELECT * FROM sum_n_product(2, 4);
 sum | prod
-----+------
   6 |    8

第 36.5.4 節 所述,這有效地為函式的返回值建立了一個匿名的記錄型別。如果給出了 RETURNS 子句,則必須指定 RETURNS record

這同樣適用於過程,例如

CREATE PROCEDURE sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$
BEGIN
    sum := x + y;
    prod := x * y;
END;
$$ LANGUAGE plpgsql;

在呼叫過程時,必須指定所有引數。對於輸出引數,在從普通 SQL 呼叫過程時可以指定 NULL

CALL sum_n_product(2, 4, NULL, NULL);
 sum | prod
-----+------
   6 |    8

但是,在從 PL/pgSQL 呼叫過程時,您應該改為為主任何輸出引數編寫一個變數;該變數將接收呼叫結果。有關詳細資訊,請參閱 第 41.6.3 節

宣告 PL/pgSQL 函式的另一種方法是使用 RETURNS TABLE,例如

CREATE FUNCTION extended_sales(p_itemno int)
RETURNS TABLE(quantity int, total numeric) AS $$
BEGIN
    RETURN QUERY SELECT s.quantity, s.quantity * s.price FROM sales AS s
                 WHERE s.itemno = p_itemno;
END;
$$ LANGUAGE plpgsql;

這與宣告一個或多個 OUT 引數並指定 RETURNS SETOF sometype 完全等價。

PL/pgSQL 函式的返回型別宣告為多型型別時(請參閱 第 36.2.5 節),會建立一個特殊的引數 $0。它的資料型別是函式的實際返回型別,由實際輸入型別推導得出。這允許函式訪問其實際返回型別,如 第 41.3.3 節 所示。$0 初始化為 null,可以被函式修改,因此它可以用來儲存返回值(如果需要),但並非必需。$0 也可以被賦予別名。例如,這個函式作用於任何具有 + 運算子的資料型別。

CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement)
RETURNS anyelement AS $$
DECLARE
    result ALIAS FOR $0;
BEGIN
    result := v1 + v2 + v3;
    RETURN result;
END;
$$ LANGUAGE plpgsql;

透過宣告一個或多個輸出引數作為多型型別可以獲得相同的效果。在這種情況下,不使用特殊的 $0 引數;輸出引數本身起到相同的作用。例如

CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement,
                                 OUT sum anyelement)
AS $$
BEGIN
    sum := v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;

實際上,使用 anycompatible 系列型別宣告一個多型函式可能更有用,這樣輸入引數就可以自動提升到公共型別。例如

CREATE FUNCTION add_three_values(v1 anycompatible, v2 anycompatible, v3 anycompatible)
RETURNS anycompatible AS $$
BEGIN
    RETURN v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;

使用這個例子,一個呼叫,例如

SELECT add_three_values(1, 2, 4.7);

將能夠工作,自動將整數輸入提升為數值型別。使用 anyelement 的函式需要您手動將三個輸入轉換為相同的型別。

41.3.2. ALIAS #

newname ALIAS FOR oldname;

ALIAS 語法比前一節暗示的更通用:您可以為任何變數宣告別名,而不僅僅是函式引數。這方面的主要實際用途是將一個不同的名稱賦給具有預定名稱的變數,例如觸發器函式中的 NEWOLD

示例

DECLARE
  prior ALIAS FOR old;
  updated ALIAS FOR new;

由於 ALIAS 建立了兩種命名同一物件的不同方式,不受限制地使用可能會令人困惑。最好僅用於覆蓋預定名稱的目的。

41.3.3. 複製型別 #

name table.column%TYPE
name variable%TYPE

%TYPE 提供表列或先前宣告的 PL/pgSQL 變數的資料型別。您可以使用它來宣告將儲存資料庫值的變數。例如,假設您的 users 表中有一個名為 user_id 的列。要宣告一個與 users.user_id 具有相同資料型別的變數,請編寫

user_id users.user_id%TYPE;

也可以在 %TYPE 之後寫陣列宣告,從而建立一個儲存該型別陣列的變數。

user_ids users.user_id%TYPE[];
user_ids users.user_id%TYPE ARRAY[4];  -- equivalent to the above

就像宣告陣列列一樣,在 %TYPE 之後寫多個方括號對還是指定陣列維度無關緊要:PostgreSQL 將給定元素型別的所有陣列視為同一型別,無論維度如何。(請參閱 第 8.15.1 節)。

透過使用 %TYPE,您不需要知道您引用的結構的
資料型別,最重要的是,如果引用項的資料型別將來發生變化(例如:您將 user_id 的型別從 integer 更改為 real),您可能不需要更改函式定義。

%TYPE 在多型函式中尤其有價值,因為內部變數所需的資料型別可能因呼叫而異。可以透過將 %TYPE 應用於函式的引數或結果佔位符來建立相應的變數。

41.3.4. 行型別 #

name table_name%ROWTYPE;
name composite_type_name;

複合型別變數稱為 變數(或 行型別變數)。這樣的變數可以儲存 SELECTFOR 查詢結果的整個行,只要該查詢的列集與變數的宣告型別匹配。使用普通的點表示法可以訪問行值的各個欄位,例如 rowvar.field

行變數可以宣告為具有現有表或檢視的行相同的型別,透過使用 table_name%ROWTYPE 表示法;或者可以透過給出複合型別的名稱來宣告。 (由於每個表都有一個同名的關聯複合型別,所以在 PostgreSQL 中,寫 %ROWTYPE 或不寫實際上沒有區別。但帶有 %ROWTYPE 的形式更具可移植性。)

%TYPE 一樣,%ROWTYPE 後面可以跟陣列宣告,以宣告一個儲存該複合型別陣列的變數。

函式的引數可以是複合型別(完整的錶行)。在這種情況下,相應的識別符號 $n 將是一個行變數,可以從中選擇欄位,例如 $1.user_id

下面是一個使用複合型別的示例。 table1table2 是已存在的表,至少包含提到的欄位。

CREATE FUNCTION merge_fields(t_row table1) RETURNS text AS $$
DECLARE
    t2_row table2%ROWTYPE;
BEGIN
    SELECT * INTO t2_row FROM table2 WHERE ... ;
    RETURN t_row.f1 || t2_row.f3 || t_row.f5 || t2_row.f7;
END;
$$ LANGUAGE plpgsql;

SELECT merge_fields(t.*) FROM table1 t WHERE ... ;

41.3.5. 記錄型別 #

name RECORD;

記錄變數與行型別變數相似,但它們沒有預定義的結構。它們在 SELECTFOR 命令執行期間,會根據被賦給它們的值而獲得實際的行結構。記錄變數的子結構每次賦值時都可以改變。因此,直到記錄變數被首次賦值之前,它沒有子結構,任何嘗試訪問其欄位的操作都會導致執行時錯誤。

請注意,RECORD 不是一個真正的資料型別,而只是一個佔位符。還應該認識到,當 PL/pgSQL 函式宣告返回型別為 record 時,這與記錄變數的概念並不完全相同,即使這樣的函式可能使用記錄變數來儲存其結果。在這兩種情況下,當編寫函式時,實際的行結構是未知的,但對於返回 record 的函式,實際結構在呼叫查詢解析時確定,而記錄變數可以在執行時改變其行結構。

41.3.6. PL/pgSQL 變數的排序規則 #

PL/pgSQL 函式有一個或多個可排序資料型別的引數時,會根據分配給實際引數的排序規則為每個函式呼叫確定一個排序規則,如 第 23.2 節 所述。如果成功確定了排序規則(即,引數之間沒有隱式排序規則衝突),則所有可排序引數將被隱式視為具有該排序規則。這將影響函式內排序敏感操作的行為。例如,考慮

CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$
BEGIN
    RETURN a < b;
END;
$$ LANGUAGE plpgsql;

SELECT less_than(text_field_1, text_field_2) FROM table1;
SELECT less_than(text_field_1, text_field_2 COLLATE "C") FROM table1;

第一個 less_than 的使用將使用 text_field_1text_field_2 的公共排序規則進行比較,而第二個使用將使用 C 排序規則。

此外,確定的排序規則也被假定為任何區域性變數的排序規則,這些區域性變數是可排序型別的。因此,此函式如果寫成

CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$
DECLARE
    local_a text := a;
    local_b text := b;
BEGIN
    RETURN local_a < local_b;
END;
$$ LANGUAGE plpgsql;

如果沒有任何可排序資料型別的引數,或者無法確定它們的公共排序規則,則引數和區域性變數將使用其資料型別的預設排序規則(通常是資料庫的預設排序規則,但對於域型別變數可能不同)。

可以透過在宣告中包含 COLLATE 選項,將與可排序資料型別的區域性變數關聯不同的排序規則,例如

DECLARE
    local_a text COLLATE "en_US";

此選項會覆蓋根據上述規則會分配給變數的排序規則。

當然,如果希望在特定操作中使用特定排序規則,也可以在函式內部顯式編寫 COLLATE 子句。例如,

CREATE FUNCTION less_than_c(a text, b text) RETURNS boolean AS $$
BEGIN
    RETURN a < b COLLATE "C";
END;
$$ LANGUAGE plpgsql;

這會覆蓋表示式中使用的表列、引數或區域性變數關聯的排序規則,就像在普通 SQL 命令中一樣。

提交更正

如果您在文件中看到任何不正確的內容,與您使用該功能的經驗不符,或者需要進一步澄清,請使用 此表單 報告文件問題。