區塊中使用的所有變數都必須在區塊的宣告區段中宣告。(唯一的例外是,在整數值範圍內迭代的 FOR
迴圈的迴圈變數會自動宣告為整數變數,同樣地,在游標結果中迭代的 FOR
迴圈的迴圈變數也會自動宣告為紀錄變數。)
PL/pgSQL 變數可以具有任何 SQL 資料類型,例如 integer
、varchar
和 char
。
以下是一些變數宣告的範例
user_id integer; quantity numeric(5); url varchar; myrow tablename%ROWTYPE; myfield tablename.columnname%TYPE; arow RECORD;
變數宣告的一般語法為
name
[ CONSTANT ]type
[ COLLATEcollation_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;
傳遞給函數的參數以識別碼 $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 函數時,輸出參數會以與一般輸入參數相同的方式給定 $
名稱和可選別名。輸出參數實際上是一個從 NULL 開始的變數;應該在函數執行期間分配給它。參數的最終值是傳回的值。例如,也可以用這種方式完成銷售稅範例n
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 函數的回傳型別宣告為多型別 (polymorphic type) 時 (請參閱第 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
的函數會要求您手動將三個輸入強制轉換為相同的型別。
ALIAS
#newname
ALIAS FORoldname
;
ALIAS
語法的用途比上一節所暗示的更廣泛:您可以為任何變數宣告別名,而不僅僅是函數參數。此功能的主要實際用途是為具有預定名稱的變數分配不同的名稱,例如觸發函數中的 NEW
或 OLD
。
範例
DECLARE prior ALIAS FOR old; updated ALIAS FOR new;
由於 ALIAS
建立了兩種不同的方式來命名相同的物件,因此不受限制的使用可能會造成混淆。最好僅將其用於覆蓋預定名稱。
name
table
.column
%TYPEname
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
正如宣告作為陣列的資料表欄位時一樣,無論您撰寫多個方括號對還是特定的陣列維度都無關緊要:PostgreSQL 將給定元素型別的所有陣列視為相同的型別,無論維度如何。(請參閱第 8.15.1 節。)
透過使用 %TYPE
,您無需知道您正在參考的結構的資料型別,最重要的是,如果引用的項目的資料型別在將來發生變化 (例如:您將 user_id
的型別從 integer
變更為 real
),您可能不需要變更您的函數定義。
%TYPE
在多型函數中特別有價值,因為內部變數所需的資料型別可能會因一次呼叫而異。透過將 %TYPE
應用於函數的引數或結果佔位符,可以建立適當的變數。
name
table_name
%ROWTYPE
;name
composite_type_name
;
複合型別的變數稱為列變數(或列型別變數)。這樣的變數可以保存 SELECT
或 FOR
查詢結果的整列,只要該查詢的欄位集與變數的宣告型別相符即可。列值的各個欄位使用常用的點表示法存取,例如 rowvar.field
。
可以透過使用 table_name
%ROWTYPE
表示法,宣告列變數具有與現有資料表或檢視表的列相同的型別;也可以透過給定複合型別的名稱來宣告。(由於每個資料表都有一個具有相同名稱的關聯複合型別,因此在 PostgreSQL 中,您撰寫 %ROWTYPE
與否實際上沒有關係。但是帶有 %ROWTYPE
的形式更具可移植性。)
與 %TYPE
一樣,%ROWTYPE
後面可以跟隨陣列裝飾,以宣告保存引用的複合型別陣列的變數。
函數的參數可以是複合型別(完整的資料表列)。在這種情況下,相應的識別符 $
將是一個列變數,並且可以從中選擇欄位,例如 n
$1.user_id
。
以下是使用複合型別的範例。table1
和 table2
是現有的資料表,至少具有提到的欄位:
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 ... ;
name
RECORD;
紀錄變數與列型別變數相似,但它們沒有預定義的結構。它們在 SELECT
或 FOR
命令期間取得分配給它們的列的實際列結構。紀錄變數的子結構每次被分配時都可以改變。這樣做的結果是,在首次將紀錄變數分配給它之前,它沒有子結構,並且任何存取其中欄位的嘗試都會引發執行時間錯誤。
請注意,RECORD
不是真正的資料型別,只是一個佔位符。還應該意識到,當 PL/pgSQL 函數被宣告為回傳型別 record
時,這與紀錄變數的概念並不完全相同,即使這樣的函數可能使用紀錄變數來保存其結果。在這兩種情況下,在編寫函數時實際的列結構都是未知的,但對於回傳 record
的函數,實際的結構是在解析呼叫查詢時確定的,而紀錄變數可以即時更改其列結構。
當 PL/pgSQL 函數有一個或多個具有定序資料類型 (collatable data types) 的參數時,會針對每個函數呼叫,根據指定給實際參數的定序 (collation),識別出一個定序,如第 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_1
和 text_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;
如果沒有具有定序功能的資料類型的參數,或者無法為它們識別出通用定序,則參數和本地變數將使用其資料類型的預設定序(通常是資料庫的預設定序,但對於網域類型 (domain types) 的變數可能不同)。
具有定序功能的資料類型的本地變數可以透過在其宣告中包含 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 命令中發生的情況一樣。
如果您在文件中發現任何不正確、與您使用特定功能時的經驗不符或需要進一步澄清的地方,請使用此表單來報告文件問題。