本文介紹了PL/SQL的聲明信息。
簡介
在一個塊中使用的所有變量必須在該塊的聲明小節中聲明(唯一的例外是在一個整數范圍上迭代的FOR
循環變量會被自動聲明為一個整數變量,并且相似地在一個游標結果上迭代的FOR
循環變量會被自動地聲明為一個記錄變量)。
PL/SQL變量可以是任意SQL數據類型,例如integer
、varchar
和char
。
這里是變量聲明的一些例子:
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
選項指定用于該變量的一個排序規則。如果指定了NOT NULL
,對該變量賦值為空值會導致一個運行時錯誤。所有被聲明為NOT NULL
的變量必須被指定一個非空默認值。 等號(=
)可以被用來代替 PL/SQL-兼容的 :=
。
一個變量的默認值會在每次進入該塊時被計算并且賦值給該變量(不是每次函數調用只計算一次)。因此,例如將now()
賦值給類型為timestamp
的一個變量將會導致該變量具有當前函數調用的時間,而不是該函數被預編譯的時間。
例子:
quantity integer DEFAULT 32;
url varchar := 'http://mysite.com';
user_id CONSTANT integer := 10;
聲明函數參數
傳遞給函數的參數被命名為標識符$1
、$2
等等。可選地,能夠為$``n
參數名聲明別名來增加可讀性。不管是別名還是數字標識符都能用來引用參數值。
有兩種方式來創建一個別名。比較好的方式是在CREATE FUNCTION
命令中為參數給定一個名稱。例如:
CREATE FUNCTION sales_tax(subtotal real) RETURN real IS
BEGIN
RETURN subtotal * 0.06;
END;
另一種方式是顯式地使用聲明語法聲明一個別名。
name ALIAS FOR $n;
使用這種風格的同一個例子看起來是:
CREATE FUNCTION sales_tax(real) RETURN real IS
DECLARE
subtotal ALIAS FOR $1;
BEGIN
RETURN subtotal * 0.06;
END;
這兩個例子并非完全等效。在第一種情況中,subtotal
可以被引用為sales_tax.subtotal
,但在第二種情況中它不能這樣引用(如果我們為內層塊附加了一個標簽,subtotal
則可以用那個標簽限定)。
更多一些例子:
CREATE FUNCTION instr(varchar, integer) RETURN integer IS
DECLARE
v_string ALIAS FOR $1;
index ALIAS FOR $2;
BEGIN
-- 這里是一些使用 v_string 和 index 的計算
END;
CREATE FUNCTION concat_selected_fields(in_t sometablename) RETURN text IS
BEGIN
RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
當一個PL/SQL函數被聲明為帶有輸出參數,輸出參數可以用普通輸入參數相同的方式被給定$``n
名稱以及可選的別名。一個輸出參數實際上是一個最初為 NULL 的變量,它應當在函數的執行期間被賦值。該參數的最終值就是要被返回的東西。例如,sales-tax 例子也可以用這種方式來做:
CREATE FUNCTION sales_tax(subtotal real, OUT tax real) IS
BEGIN
tax := subtotal * 0.06;
END;
我們忽略了RETURNS real
— 我們也可以包括它,但是那將是冗余。
當返回多個值時,輸出參數最有用。一個小例子是:
CREATE FUNCTION sum_n_product(x int, y int, OUT sum int, OUT prod int) IS
BEGIN
sum := x + y;
prod := x * y;
END;
這實際上為該函數的結果創建了一個匿名記錄類型。如果給定了一個RETURNS
子句,它必須RETURNS record
。
聲明一個PL/SQL函數的另一種方式是用RETURNS TABLE
,例如:
CREATE FUNCTION extended_sales(p_itemno int)
RETURN TABLE(quantity int, total numeric) IS
BEGIN
RETURN QUERY SELECT s.quantity, s.quantity * s.price FROM sales AS s
WHERE s.itemno = p_itemno;
END;
這和聲明一個或多個OUT
參數并且指定RETURNS SETOF ``sometype
完全等效。
當PL/SQL函數的返回類型被聲明為多態類型時, 一個特殊的參數 $0
已創建。它的數據類型是函數的實際返回類型,從實際輸入類型推導出來。 $0
被初始化為空并且不能被該函數修改,因此它能夠被用來保持可能需要的返回值,不過這不是必須的。 $0
也可以被給定一個別名。例如,這個函數工作在任何具有一個+
操作符的數據類型上:
CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement)
RETURN anyelement IS
DECLARE
result ALIAS FOR $0;
BEGIN
result := v1 + v2 + v3;
RETURN result;
END;
通過聲明一個或多個輸出參數為多態類型可以得到同樣的效果。在這種情況下,不使用特殊的$0
參數,輸出參數本身就用作相同的目的。例如:
CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement,
OUT sum anyelement)
IS
BEGIN
sum := v1 + v2 + v3;
END;
在實踐中,使用anycompatible
類型系列聲明多態函數可能更有用,以便將輸入參數自動提升為公共類型。例如:
CREATE FUNCTION add_three_values(v1 anycompatible, v2 anycompatible, v3 anycompatible)
RETURN anycompatible IS
BEGIN
RETURN v1 + v2 + v3;
END;
在此示例中,調用如
SELECT add_three_values(1, 2, 4.7);
將工作,自動將整數輸入提升為數字。使用anyelement
的函數需要您手動將三個輸入轉換為相同的類型。
ALIAS
newname ALIAS FOR oldname;
ALIAS
語法比前一節中建議的更一般化:你可以為任意變量聲明一個別名,而不只是函數參數。其主要實際用途是為預先決定了名稱的變量分配一個不同的名稱,例如在一個觸發器過程中的NEW
或OLD
。
例子:
DECLARE
prior ALIAS FOR old;
updated ALIAS FOR new;
因為ALIAS
創造了兩種不同的方式來命名相同的對象,如果對其使用不加限制就會導致混淆。最好只把它用來覆蓋預先決定的名稱。
復制類型
variable%TYPE
%TYPE
提供了一個變量或表列的數據類型。你可以用它來聲明將保持數據庫值的變量。例如,如果你在users
中有一個名為user_id
的列。要定義一個與users.user_id
具有相同數據類型的變量:
user_id users.user_id%TYPE;
通過使用%TYPE
,你不需要知道你要引用的結構的實際數據類型,而且最重要的,如果被引用項的數據類型在未來被改變(例如你把user_id
的類型從integer
改為real
),你不需要改變你的函數定義。
%TYPE
在多態函數中特別有價值,因為內部變量所需的數據類型能在兩次調用時改變。可以把%TYPE
應用在函數的參數或結果占位符上來創建合適的變量。
行類型
name table_name%ROWTYPE;
name composite_type_name;
一個組合類型的變量被稱為一個行變量(或行類型變量)。這樣一個變量可以保持一個SELECT
或FOR
查詢結果的一整行,前提是查詢的列集合匹配該變量被聲明的類型。該行值的各個域可以使用通常的點號標記訪問,例如rowvar.field
。
通過使用table_name``%ROWTYPE
標記,一個行變量可以被聲明為具有和一個現有表或視圖的行相同的類型。它也可以通過給定一個組合類型名稱來聲明(因為每一個表都有一個相關聯的具有相同名稱的組合類型,所以在本數據庫中實際上寫不寫%ROWTYPE
都沒有關系。但是帶有%ROWTYPE
的形式可移植性更好)。
一個函數的參數可以是組合類型(完整的表行)。在這種情況下,相應的標識符$``n
將是一個行變量,并且可以從中選擇域,例如$1.user_id
。
這里是一個使用組合類型的例子。table1
和table2
是已有的表,它們至少有以下提到的域:
CREATE FUNCTION merge_fields(t_row table1) RETURN text IS
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;
SELECT merge_fields(t.*) FROM table1 t WHERE ... ;
記錄類型
name RECORD;
記錄變量和行類型變量類似,但是它們沒有預定義的結構。它們采用在一個SELECT
或FOR
命令期間為其賦值的行的真實行結構。一個記錄變量的子結構能在每次它被賦值時改變。這樣的結果是直到一個記錄變量第一次被賦值之前,它都沒有子結構,并且任何嘗試訪問其中一個域都會導致一個運行時錯誤。
RECORD
并非一個真正的數據類型,只是一個占位符。我們也應該認識到當一個PL/SQL函數被聲明為返回類型record
,這與一個記錄變量并不是完全相同的概念,即便這樣一個函數可能會用一個記錄變量來保持其結果。在兩種情況下,編寫函數時都不知道真實的行結構,但是對于一個返回record
的函數,當調用查詢被解析時就已經決定了真正的結構,而一個行變量能夠隨時改變它的行結構。
PL/SQL變量的排序規則
當一個PL/SQL函數有一個或多個可排序數據類型的參數時,為每一次函數調用都會基于賦值給實參的排序規則來確定出一個排序規則。如果一個排序規則被成功地確定(即在參數之間隱式排序規則沒有沖突),那么所有的可排序參數會被當做隱式具有那個排序規則。這將在函數中影響行為受到排序規則影響的操作。例如,考慮
CREATE FUNCTION less_than(a text, b text) RETURN boolean IS
BEGIN
RETURN a < b;
END;
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) RETURN boolean IS
DECLARE
local_a text := a;
local_b text := b;
BEGIN
RETURN local_a < local_b;
END;
如果沒有可排序數據類型的參數,或者不能為它們確定共同的排序規則,那么參數和本地變量會使用它們數據類型的默認排序規則(通常是數據庫的默認排序規則,但是可能不同于域類型的變量)。
通過在一個可排序數據類型的本地變量的聲明中包括COLLATE
選項,可以為它指定一個不同的排序規則,例如
DECLARE
local_a text COLLATE "en_US";
這個選項會覆蓋根據上述規則被給予該變量的排序規則。
還有,如果一個函數想要強制在一個特定操作中使用一個特定排序規則,當然可以在該函數內部寫一個顯式的COLLATE
子句。例如:
CREATE FUNCTION less_than_c(a text, b text) RETURN boolean IS
BEGIN
RETURN a < b COLLATE "C";
END;
這會覆蓋表達式中使用的表列、參數或本地變量相關的排序規則,就像在純 SQL 命令中發生的一樣。