?
? ????? PHP ??? ???? ??? ?? ??
All variables used in a block must be declared in the declarations section of the block. (The only exceptions are that the loop variable of a FOR loop iterating over a range of integer values is automatically declared as an integer variable, and likewise the loop variable of a FOR loop iterating over a cursor's result is automatically declared as a record variable.) 所有在塊里使用的變量都必須在一個(gè)塊的聲明段里聲明。 唯一的例外是一個(gè)FOR循環(huán)里的循環(huán)變量是在一個(gè)整數(shù)范圍內(nèi)迭代的, 被自動(dòng)聲明為整數(shù)變量。
PL/pgSQL變量可以使用任意的 SQL 數(shù)據(jù)類型, 比如integer, varchar, char等等。
下面是一些變量聲明的例子:
user_id integer; quantity numeric(5); url varchar; myrow tablename%ROWTYPE; myfield tablename.columnname%TYPE; arow RECORD;
一個(gè)變量聲明的一般性語(yǔ)法是:
name [ CONSTANT ] type [ NOT NULL ] [ { DEFAULT | := } expression ];
如果給出了DEFAULT子句,那么它聲明了在進(jìn)入該塊的時(shí)候賦予該變量的初始值。 如果沒(méi)有給出DEFAULT子句,那么該變量初始化為SQL NULL。 CONSTANT選項(xiàng)避免了該變量被賦值,這樣其數(shù)值在該塊的范圍內(nèi)保持常量。 如果聲明了NOT NULL,那么賦予NULL的數(shù)值將運(yùn)行時(shí)導(dǎo)致錯(cuò)誤。 所以所有聲明為NOT NULL的變量還必須聲明一個(gè)非空的缺省值。
缺省值是在每次進(jìn)入該塊的時(shí)候計(jì)算的,而不是每次調(diào)用函數(shù)時(shí)。 因此,如果把now()賦予一個(gè)類型為 timestamp 的變量會(huì)令變量擁有函數(shù)實(shí)際調(diào)用的時(shí)間, 而不是函數(shù)預(yù)編譯的時(shí)間。
例如:
quantity integer DEFAULT 32; url varchar := 'http://mysite.com'; user_id CONSTANT integer := 10;
傳遞給函數(shù)的參數(shù)都是用 $1, $2等等這樣的標(biāo)識(shí)符。 為了增加可讀性,可以為$n參數(shù)名聲明別名。 然后別名或者數(shù)字標(biāo)識(shí)符都可以指向參數(shù)值。
有兩種創(chuàng)建別名的方法, 比較好的是在CREATE FUNCTION命令里給出參數(shù)名, 比如:
CREATE FUNCTION sales_tax(subtotal real) RETURNS real AS $$ BEGIN RETURN subtotal * 0.06; END; $$ LANGUAGE plpgsql;
另外一個(gè)方法(也是PostgreSQL以前的唯一的方法), 是使用聲明語(yǔ)法明確的聲明:
name ALIAS FOR $n;
這個(gè)風(fēng)格的同一個(gè)例子看起來(lái)像下面這樣
CREATE FUNCTION sales_tax(real) RETURNS real AS $$ DECLARE subtotal ALIAS FOR $1; BEGIN RETURN subtotal * 0.06; END; $$ LANGUAGE plpgsql;
Note: (Had we attached a label to the inner block, subtotal could be qualified with that label, instead.) 這兩個(gè)例子的作用不是完全一致的。在第一個(gè)例子中,subtotal可以作為sales_tax.subtotal被引用,而在第二個(gè)例子中是不可以的。
更多例子:
CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$ DECLARE v_string ALIAS FOR $1; index ALIAS FOR $2; BEGIN —-這里放一些使用 v_string 和 index 的計(jì)算 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;
如果一個(gè)PL/pgSQL函數(shù)聲明中含有輸出參數(shù), 那么就會(huì)給予輸出參數(shù)$n的名字以及可選的別名, 方法和其它正常輸入?yún)?shù)一樣。 一個(gè)輸出參數(shù)實(shí)際上是初始值為 NULL 的變量; 在函數(shù)執(zhí)行的過(guò)程中,應(yīng)該給它賦值。 該參數(shù)的最后數(shù)值是返回的東西。 比如,銷售額-稅費(fèi)的例子也可以這么做:
CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$ BEGIN tax := subtotal * 0.06; END; $$ LANGUAGE plpgsql;
請(qǐng)注意忽略了RETURNS real,當(dāng)然也可以包含它, 不過(guò)這樣就顯得多余了。
、 輸出參數(shù)在返回多個(gè)數(shù)值的時(shí)候非常有用。 一個(gè)簡(jiǎn)單的例子是:
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;
正如在Section 35.4.4里面討論的, 這樣做實(shí)際上為函數(shù)的結(jié)果創(chuàng)建了一個(gè)匿名的記錄類型。 如果給出一個(gè)RETURNS子句, 那么它就必須使用 RETURNS record。
另一個(gè)聲明PL/pgSQL函數(shù)的方法是使用RETURNS TABLE,例如:
CREATE FUNCTION extended_sales(p_itemno int) RETURNS TABLE(quantity int, total numeric) AS $$ BEGIN RETURN QUERY SELECT quantity, quantity * price FROM sales WHERE itemno = p_itemno; END; $$ LANGUAGE plpgsql;
這完全等價(jià)于聲明一個(gè)或多個(gè)OUT參數(shù),并且聲明RETURNS SETOF sometype。
如果將 PL/pgSQL 函數(shù)的返回類型聲明為多態(tài)類型 (anyelement,anyarray, anynonarray, 或 anyenum), 那么就會(huì)創(chuàng)建一個(gè)特殊的$0參數(shù), 它的數(shù)據(jù)類型是函數(shù)的實(shí)際返回類型, 和從實(shí)際輸入類型的推導(dǎo)類型一樣(參閱節(jié)Section 35.2.5)。 這樣就允許函數(shù)像節(jié)Section 39.3.3里顯示的那樣訪問(wèn)它的實(shí)際返回類型。 $0初始化為空,并且可以被函數(shù)修改, 所以,如果需要,它可以用于保存返回值,雖然這并非必須。 $0還可以給予一個(gè)別名。 比如,這個(gè)函數(shù)可以在任何有+操作符的數(shù)據(jù)類型上運(yùn)轉(zhuǎn):
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;
通過(guò)將一個(gè)或多個(gè)output參數(shù)聲明為多態(tài)類型,可以達(dá)到相同的效果。 在這種情況下,特殊的參數(shù)$0不會(huì)使用;輸出參數(shù)自己起這個(gè)作用。比如:
CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement, OUT sum anyelement) AS $$ BEGIN sum := v1 + v2 + v3; END; $$ LANGUAGE plpgsql;
newname ALIAS FOR oldname;
別名(ALIAS)語(yǔ)法比在之前章節(jié)提到的更普遍:可以為任何一個(gè)參數(shù)聲明別名,而不僅僅只是對(duì)函數(shù)。 這樣做的主要目的是為已經(jīng)有名字的參數(shù)重新定義一個(gè)名字,例如觸發(fā)器中的NEW或OLD。
例如
DECLARE prior ALIAS FOR old; updated ALIAS FOR new;
由于ALIAS創(chuàng)建了兩種不同的方式來(lái)命名相同的對(duì)象,因此,無(wú)限制的使用會(huì)造成混淆。 最好是在重寫預(yù)定名稱時(shí)使用。
variable%TYPE
%TYPE提供一個(gè)變量或者表字段的數(shù)據(jù)類型。 你可以用這個(gè)聲明將要保存數(shù)據(jù)庫(kù)數(shù)值的變量。 比如,假如你在users表里面有一個(gè)user_id字段。 要聲明一個(gè)和users.user_id類型相同的變量, 可以這樣寫:
user_id users.user_id%TYPE;
通過(guò)使用%TYPE,你無(wú)需知道引用的結(jié)構(gòu)的數(shù)據(jù)類型, 并且,最重要的是, 如果被引用項(xiàng)的數(shù)據(jù)類型在將來(lái)變化了(比如把user_id的類型從integer改成real), 也不需要修改函數(shù)定義。
%TYPE對(duì)多態(tài)函數(shù)特別有用, 因?yàn)閮?nèi)部變量的數(shù)據(jù)類型可能在不同調(diào)用中不一樣。 可以通過(guò)給函數(shù)的參數(shù)或者結(jié)果占位符附加%TYPE的方法來(lái)創(chuàng)建合適的變量。
name table_name%ROWTYPE; name composite_type_name;
一個(gè)復(fù)合類型變量叫做行變量(或者row-type變量)。 這樣的一個(gè)變量可以保存一次SELECT或 FOR命令結(jié)果的完整一行, 只要命令的字段集匹配該變量聲明的類型。 行數(shù)值的字段使用點(diǎn)表示法訪問(wèn),比如rowvar.field。
行變量可以聲明為和一個(gè)現(xiàn)有的表或者視圖的行類型相同, 方法是使用table_name%ROWTYPE表示法; 或者你也可以聲明它的類型是一個(gè)復(fù)合類型的名字。 因?yàn)槊總€(gè)表都有一個(gè)相關(guān)聯(lián)的同名數(shù)據(jù)類型, 在PostgreSQL里實(shí)在是無(wú)所謂你寫不寫%ROWTYPE。 但是有%ROWTYPE的形式移植性更好。
函數(shù)的參數(shù)可以是復(fù)合類型(表的完整行)。 這個(gè)時(shí)候,對(duì)應(yīng)的標(biāo)識(shí)符$n將是一個(gè)行變量, 并且可以從中選取字段,比如$1.user_id。
在一個(gè)行類型的變量中,只可以訪問(wèn)用戶定義的表中行的屬性, 不包括 OID 或者其它系統(tǒng)屬性(因?yàn)樵撔锌赡軄?lái)自一個(gè)視圖)。 該行類型的數(shù)據(jù)域繼承表中像char(n)這種類型字段的尺寸和精度。
這里是一個(gè)使用復(fù)合類型的例子。 table1和table2是現(xiàn)有的表,至少包含代碼中提到的字段:
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;
紀(jì)錄變量類似行類型變量,但是它們沒(méi)有預(yù)定義的結(jié)構(gòu)。 它們?cè)?SELECT或FOR命令中獲取實(shí)際的行結(jié)構(gòu)。 一個(gè)行變量的子結(jié)構(gòu)可以在每次賦值的時(shí)候改變。 這樣做的一個(gè)結(jié)果是:在一個(gè)記錄變量被賦予數(shù)值之前,它沒(méi)有子結(jié)構(gòu), 并且任何對(duì)其中的數(shù)據(jù)域進(jìn)行訪問(wèn)的企圖都將產(chǎn)生一個(gè)運(yùn)行時(shí)錯(cuò)誤。
請(qǐng)注意,RECORD不是真正的數(shù)據(jù)類型,只是一個(gè)占位符。 還應(yīng)該意識(shí)到在把一個(gè)PL/pgSQL函數(shù)聲明為返回record類型的時(shí)候, 它和一個(gè)記錄變量的概念并不完全相同, 即使這個(gè)函數(shù)可能使用一個(gè)記錄變量保存它的結(jié)果也如此。 在這兩種情況下書寫函數(shù)的時(shí)候,實(shí)際的行結(jié)構(gòu)都是未知的, 但是對(duì)于返回record的函數(shù)來(lái)說(shuō), 實(shí)際的結(jié)構(gòu)是在調(diào)用它的查詢被分析的時(shí)候決定的, 而行變量可以在運(yùn)行中改變其行結(jié)構(gòu)。