一、存儲過程定義:
存儲過程(Stored Procedure)是在大型數據庫系統中,一組為了完成特定功能的SQL 語句集,它存儲在數據庫中,一次編譯后永久有效,用戶通過指定存儲過程的名字並給出參數(如果該存儲過程帶有參數)來執行它。存儲過程是數據庫中的一個重要對象。在數據量特別龐大的情況下利用存儲過程能達到倍速的效率提升。
二、存儲過程的結構
案例: 創建一個求長方形面積的存儲過程。
create or replace function area_of_rectangle(lenth integer,height integer) --存儲過程名稱與參數【參數格式:(變量名1 變量類型 , 變量名2 變量類型,…) returns integer as --有“s” $$ declare --declare表示聲明變量,可以聲明多個變量 area integer := 0; --定義面積變量數據類型 begin area := lenth * height; --主邏輯與返回值 return area; --返回值
end $$ language 'plpgsql';
注意:兩個 $$ 符中間可以填入符合命名規則的任意字符,如$body$、$aaaa$。但是下方的美元符必須與這里的保持一致。
調用存儲過程:
二、變量使用
1.變量類型:存儲過程中,對變量賦值需要兩個值類型一致;
注意:record類型變量是“記錄類型”的變量,用於存儲多行多列的值。
按官方文檔的說明,record類型的變量並不是真正的變量,該類型變量在第一次賦值前,它有多少列、每一列是什么類型都是不確定的。在第一次賦值后,該變量就根據值自動確定列的數量和各列的類型。
三、賦值
3.1、靜態賦值:
student_name := '張靜';
3.2、動態賦值:
select name into student_name from class where stu_No = 1;
--或者
execute 'select name from class where stu_No = 1' into student_name;
四、基本流程語句:存儲過程中,使用RAISE NOTICE可以在運行時將變量輸出顯示
4.1、if語句
IF ... THEN ... END IF; IF ... THEN ... ELSE ... END IF; IF ... THEN ... ELSE ... THEN ... ELSE ... END IF; --例: if student_name = '張靜' then RAISE NOTICE '我是張靜'; else if student_name like '%李%' then RAISE NOTICE '我姓李'; else RAISE NOTICE '我不是張靜,也不姓李';
4.2、case語句
CASE ... WHEN ... THEN ... ELSE ... END CASE; CASE WHEN ... THEN ... ELSE ... END CASE; --例: case student_name when '張靜','曉靜' then RAISE NOTICE '張靜和曉靜都是我的名稱'; else RAISE NOTICE '你叫錯名字了'; end case;
--例: case when student_name = '張靜' or student_name = '曉靜' then RAISE NOTICE '張靜和曉靜都是我的名稱'; else RAISE NOTICE '你叫錯名字了'; end case;
4.3、循環
[ <<label>> ] LOOP 循環體語句; EXIT [ label ] [ WHEN 判斷條件表達式 ]; END LOOP [ label ]; --例-計算1到100的和: sum := 0; i := 0; loop i := i + 1; sum := sum + i; exit when i = 100 ; end loop; RAISE NOTOCE '1到100的和為:%',sum;
[ <<label>> ] WHILE 判斷條件表達式 LOOP 循環體語句; END LOOP [ label ]; --例 - 計算1到100的和: sum := 0; i := 1; while i<=100 loop sum := sum + i; i := i + 1; end loop RAISE NOTOCE '1到100的和為:%',sum;
[ <<label>> ] FOR 循環控制變量 IN [ REVERSE ] 循環范圍 [ BY expression ] LOOP 循環體語句; END LOOP [ label ]; --計算1到100的和: --例1 - 循環執行過程類似於:for(i=1;i<=100;i++){} sum := 0; for i in 1..100 loop sum := sum + i; end loop; RAISE NOTOCE '1到100的和為:%',sum; --例2 - 循環執行過程類似於:for(i=100;i>=1;i--){} sum := 0; for i in REVERSE 100..1 loop sum := sum + i; end loop; RAISE NOTOCE '1到100的和為:%',sum; --計算1到100之間所有奇數的和 --例3 - 循環執行過程類似於:for(i=1;i<=100;i=i+2){} sum := 0; for i in 1..100 by 2 loop sum := sum + i; end loop; RAISE NOTOCE '1到100的和為:%',sum;
[ <<label>> ] FOR 變量 IN 查詢語句 LOOP 循環體語句; END LOOP [ label ]; --例 - 遍歷班級中每個人的名字: for student_name in select name from class loop RAISE NOTICE '姓名:%',student_name; end loop;
四、查詢並返回多條記錄
案例1:
create or replace function f_get_member_info(id integer) returns setof record as --setof是關鍵字,暫時不清楚其作用;record是返回的數據類型,即記錄類型數據; $$ --兩個美元符必須存在,中間可以填入符合命名規則的字符(如$body$,$abc$),但必須與下方的兩個美元符相統一 declare rec record; --定義記錄類型的變量,用於存儲查詢的結果 begin --開始for循環,執行SELECT語句。注意,loop后沒有分號! for rec in EXECUTE 'SELECT id,real_name FROM a_account_all' loop return next rec; --將查詢結果依次追加到rec變量中 end loop; --for循環結束 return; end $$ language 'plpgsql'; --調用存儲過程f_get_member_info示例 -- a_account_all 為存儲過程中被查詢的表,id和real_name是表中的字段,也是在存儲過程中被查詢的字段
SELECT * FROM f_get_member_info(1568) as a_account_all(id integer,real_name character varying(50));
CREATE OR REPLACE FUNCTION public.test() RETURNS SETOF record LANGUAGE 'plpgsql' --這三句話我也還沒搞懂其含義,但加上不會報錯 COST 100 VOLATILE ROWS 1000 AS $BODY$ DECLARE temp_rec record ;--第一個記錄集 rec record ;--第二個記錄集 BEGIN --給第一個結果集賦值 for temp_rec in execute 'SELECT 1,2,3' loop return next temp_rec; RAISE NOTICE 'temp_rec:%',temp_rec; end loop; --給第二個結果集賦值 for rec in execute 'SELECT 4,5,6' loop return next rec; RAISE NOTICE 'rec:%',rec; end loop; --給第二個結果集追加值 rec := (9,8,7); return next rec; --將兩個結果集一起返回 return ; END $BODY$; ALTER FUNCTION public.test() OWNER TO postgres; -- 調用示例: SELECT * FROM test() as temp1(num_1 integer,num_2 integer,num_3 integer);
實戰案例:獲取數據表中ID最大值:
CREATE TABLE department( ID INT PRIMARY KEY NOT NULL, d_code VARCHAR(50), d_name VARCHAR(50) NOT NULL, d_parentID INT NOT NULL DEFAULT 0 ); insert into department values(1,'001','office'); insert into department values(2,'002','office',1);
create or replace function f_getNewID(myTableName text,myFeildName text) returns integer as $$ declare mysql text; myID integer; begin mysql:='select max(' || quote_ident(myFeildName) ||') from ' || quote_ident(myTableName); execute mysql into myID; --using myTableName,myFeildName; if myID is null or myID=0 then return 1; else return myID+1; end if; end; $$ language plpgsql;
調用:
select f_getnewid('department','id');
總結一下:
1、存儲過程(FUNCITON)變量可以直接用 || 拼接。上面沒有列出,下面給個栗子;
create or replace function f_getNewID(myTableName text,myFeildName text) returns integer as $$ declare mysql text; myID integer; begin mysql:='select max('|| $2 || ' ) from '||$1; execute mysql into myID using myFeildName,myTableName; if myID is null or myID=0 then return 1; else return myID+1; end if; end; $$ language plpgsql;
2、存儲過程的對象不可以直接用變量,要用 quote_ident(objVar)
3、$1 $2是 FUNCTION 參數的順序,如1中的 $1 $2交換,USING 后面的不換 結果 :select max(myTableName) from myFeildname
4、注意:SQL語句中的大寫全部會變成小寫,要想大寫存大,必須要用雙引號。
總結:
1. 運行速度:對於很簡單的 sql ,存儲過程沒有什么優勢。對於復雜的業務邏輯,因為在存儲過程創建的時候,數據庫已經對其進行了一次解析和優化。存儲過程一旦執行,在內存中就會保留一份這個存儲過程,這樣下次再執行同樣的存儲過程時,可以從內存中直接調用,所以執行速度會比普通 s ql 快。
2. 減少網絡傳輸:存儲過程直接就在數據庫服務器上跑,所有的數據訪問都在數據庫服務器內部進行,不需要傳輸數據到其它服務器,所以會減少一定的網絡傳輸。但是在存儲過程中沒有多次數據交互,那么實際上網絡傳輸量和直接 sql 是一樣的。而且我們的應用服務器通常與數據庫是在同一內網,大數據的訪問的瓶頸會是硬盤的速度,而不是網速。
3. 可 維護性:的存儲過程有些時候比程序更容易維護,這是因為可以實時更新 DB 端的存儲過程 。 有些 bug ,直接改存儲過程里的業務邏輯,就搞定了。
4. 增強安全性:提高代碼安全,防止 SQL 注入 。這一點 sql 語句也可以做到。
5. 可擴展性:應用程序和數據庫操作分開,獨立進行,而不是相互在一起。方便以后的擴展和 DBA 維護優化。
https://blog.csdn.net/Mr_Door/article/details/102527225