局部程序
本文介紹了局部程序(subprogram)的聲明和使用方法等相關(guān)內(nèi)容。
簡介
程序包括函數(shù)和過程。在PL/SQL塊中創(chuàng)建的程序被稱作嵌套子程序。您可以在PL/SQL塊(可以是另一個子程序)、包或全局級別創(chuàng)建子程序。您可以同時聲明和定義程序,也可以先聲明,然后在同一個塊中后續(xù)定義(即前向聲明)。可以使用 CREATE FUNCTION
或 CREATE PROCEDURE
聲明一個全局程序,或是在一個包中聲明和定義程序(通常是在包頭聲明程序,在包體定義程序)。這兩種程序會被存儲在系統(tǒng)表中。此外,其他嵌套子程序在這里將其稱作局部程序。它不會被存入系統(tǒng)表,當嵌套塊中調(diào)用局部程序的時候,局部程序才會被編譯和執(zhí)行。
局部程序的聲明
您可以通過以下語法聲明局部程序。
DECLARE -- 外層匿名塊,當然也可以是全局過程
[FUNCTION] -- 局部函數(shù)
name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] )
{RETURNS | RETURN} rettype
{AS | IS}
[DECLARE] -- 局部函數(shù)聲明段
...
BEGIN -- 局部函數(shù)程序段
...
RETURN ...
END;
BEGIN -- 外層的程序段
END;
您也可以將 FUNCTION
替換為 PROCEDURE
來聲明一個局部過程,并且不需要返回任何值。
局部程序的使用
以下為一個簡單的使用局部程序的示例。
DECLARE
a INT;
FUNCTION local_func RETURN INT -- 聲明局部函數(shù)
IS
BEGIN
RETURN 10;
END;
BEGIN
a := local_func(); -- 調(diào)用局部函數(shù)
RAISE NOTICE 'a: %', a;
END;
由于函數(shù)可以被重載(同一個函數(shù)名,不同的參數(shù)列表),通常情況下,系統(tǒng)將會使用 local_func
這個名稱去系統(tǒng)表中找到一系列候選函數(shù),然后從中選擇參數(shù)列表最為匹配的來執(zhí)行(當然,也可能沒有任何函數(shù)被匹配到)。在此之前,系統(tǒng)會優(yōu)先嘗試尋找匹配的局部函數(shù)。由于函數(shù)可以嵌套,系統(tǒng)將會一層一層的向上查找匹配的局部函數(shù),如下所示:
DECLARE
PROCEDURE local_proc1 IS
BEGIN
RAISE NOTICE 'call outer local_procedure1';
END;
PROCEDURE local_proc2 IS
proc2_str VARCHAR(50);
FUNCTION local_func RETURN VARCHAR IS
func_str VARCHAR(50) := 'raise inner local_func';
BEGIN
local_proc1(); -- 尋找到外層的 local_proc1
RETURN func_str;
END;
BEGIN
proc2_str := local_func();
raise notice '%', proc2_str;
END;
BEGIN
local_proc2();
END;
結(jié)果顯示如下:
NOTICE: call outer local_procedure1
NOTICE: raise inner local_func
如果在某一層尋找到至少一個同名的局部函數(shù)后,將會停止繼續(xù)向上搜索,并在當前層的所有局部函數(shù)中選擇出一個優(yōu)勝者函數(shù)來執(zhí)行。此時,如果已經(jīng)找到同名的局部函數(shù)但無法選出優(yōu)勝者,那么將直接報錯。
DECLARE
PROCEDURE local_proc1 IS
BEGIN
RAISE NOTICE 'call outer local_procedure1';
END;
PROCEDURE local_proc2 IS
proc2_str VARCHAR(50);
FUNCTION local_func RETURN VARCHAR IS
func_str VARCHAR(50) := 'raise inner local_func';
BEGIN
local_proc1(1); -- 尋找到外層的 local_proc1, 但無法匹配
RETURN func_str;
END;
BEGIN
proc2_str := local_func();
raise notice '%', proc2_str;
END;
BEGIN
local_proc2();
END;
結(jié)果顯示如下:
ERROR: wrong number or types of arguments in call to local function local_proc1
前向聲明
如果同一個PL/SQL塊中的嵌套子程序相互調(diào)用,則需要前向聲明,因為必須先聲明子程序,然后才能調(diào)用子程序。如果聲明后沒有在同一個PL/SQL塊中定義子程序,系統(tǒng)會檢查出這種錯誤。
DECLARE
FUNCTION func_test(id INT) return INT; -- 只聲明,未定義
BEGIN
END;
結(jié)果顯示如下:
ERROR: subroutine body must be defined for forward-declared function "func_test"
以下示例展示如何正確使用前向聲明。
DECLARE
PROCEDURE proc1(number1 NUMBER); -- 前向聲明 proc1
PROCEDURE proc2(number2 NUMBER) IS -- 聲明并定義 proc2
BEGIN
proc1(number2); -- 調(diào)用 proc1
END;
-- Define proc 1:
PROCEDURE proc1(number1 NUMBER) IS -- 定義 proc1
BEGIN
proc2 (number1); -- 調(diào)用 proc2
END;
BEGIN
NULL;
END;
您必須保證嵌套調(diào)用有正確的退出條件,否則會在反復(fù)嵌套調(diào)用中超過最大調(diào)用棧而報錯。
使用外層程序的變量
在局部程序的使用中,給出了一個使用外層局部函數(shù)的示例。同理,在局部程序之前聲明的所有變量、游標、自定義異常等等都是可以直接使用的。您可以對這些變量取值、賦值,開啟游標并從中取行,或是使用外層的自定義異常來拋出一個異常。以下是一個說明變量可見性的示例。
DECLARE
a INT := 1; -- 可以使用 a
PROCEDURE local_proc IS
BEGIN
RAISE NOTICE 'call outer local_procedure';
RAISE NOTICE 'inner raise a: %', a;
a := 10;
END;
b INT; -- 無法使用 b
BEGIN
RAISE NOTICE 'outer raise a: %', a;
local_proc();
RAISE NOTICE 'outer raise a: %', a;
END;
結(jié)果顯示如下:
NOTICE: outer raise a: 1
NOTICE: call outer local_procedure
NOTICE: inner raise a: 1
NOTICE: outer raise a: 10
如果您嘗試使用在 local_proc
之后聲明的變量,則會出現(xiàn)語法錯誤。
DECLARE
a INT := 1; -- 可以使用 a
PROCEDURE local_proc IS
BEGIN
RAISE NOTICE 'call outer local_procedure';
RAISE NOTICE 'inner raise a: %', a;
a := 10;
END;
b INT; -- 無法使用 b
BEGIN
local_proc();
END;
結(jié)果顯示如下:
ERROR: "b" is not a known variable