如何理解postgresql的存儲過程

在b/s系統的構建過程中,數據庫操作幾乎成為了一個必不可少的操作。調用存儲過程實現數據庫操作使很多程序員使用的方法,而且大多數的程序員都是能使用存儲過程就使用存儲過程,很少直接使用sql語句,所以存儲過程是很有用而且很重要的。

存儲過程簡介

存儲過程的定義:完成一定功能的可重復調用的程序

簡單的說,存儲過程是由一些sql語句和控制語句組成的被封裝起來的過程,它駐留在數據庫中,可以被客戶應用程序調用,也可以從另一個過程或觸發器調用。它的參數可以被傳遞和返回。與應用程序中的函數過程類似,存儲過程可以通過名字來調用,而且它們同樣有輸入參數和輸出參數。

根據返回值類型的不同,我們可以將存儲過程分為三類:返回記錄集的存儲過程,返回數值的存儲過程(也可以稱為標量存儲過程),以及行為存儲過程。顧名思義,返回記錄集的存儲過程的執行結果是一個記錄集,典型的例子是從數據庫中檢索出符合某一個或幾個條件的記錄;返回數值的存儲過程執行完以后返回一個值,例如在數據庫中執行一個有返回值的函數或命令;最后,行為存儲過程僅僅是用來實現數據庫的某個功能,而沒有返回值,例如在數據庫中的更新和刪除操作。

存儲過程的通俗解釋:你使用手機撥打A同事的手機,需要一個一個號碼的輸入,然后才能撥打
而如果你把這個號碼設置為快速撥號,那么你只要長按1(自己設置的數字鍵)就可以直接撥打電話了
把這個號碼設置為快速撥號的過程你就可以理解為創建存儲過程。

使用存儲過程的好處

相對于直接使用sql語句,在應用程序中直接調用存儲過程有以下好處:

(1)減少網絡通信量。調用一個行數不多的存儲過程與直接調用sql語句的網絡通信量可能不會有很大的差別,可是如果存儲過程包含上百行sql語句,那么其性能絕對比一條一條的調用sql語句要高得多。

(2)執行速度更快。有兩個原因:首先,在存儲過程創建的時候,數據庫已經對其進行了一次解析和優化。其次,存儲過程一旦執行,在內存中就會保留一份這個存儲過程,這樣下次再執行同樣的存儲過程時,可以從內存中直接調用。

(3)更強的適應性:由于存儲過程對數據庫的訪問是通過存儲過程來進行的,因此數據庫開發人員可以在不改動存儲過程接口的情況下對數據庫進行任何改動,而這些改動不會對應用程序造成影響。

(4) 布式工作:應用程序和數據庫的編碼工作可以分別獨立進行,而不會相互壓制。

由以上的分析可以看到,在應用程序中使用存儲過程是很有必要的。

存儲過程的定義規范

一,存儲過程的格式

Create or replace function 過程名(參數名 參數類型,…..) returns 返回值類型 as
                   $body$
 
                            //聲明變量
                            Declare
                            變量名變量類型;
                            如:
                            flag Boolean;
 
                            變量賦值方式(變量名類型 :=值;)
                            如:
                            str  text :=值; / str  text;  str :=值;
 
                            Begin
                                     函數體;
 
                             return 變量名; //存儲過程中的返回語句
 
                            End;
                   $body$
         Language plpgsql;

二,變量類型
有整數類型(Smallint,Int等),浮點類型(float),時間類型(date,time,timestamp,interval),還有其他特殊類型比如inet,point等。
三,連接字符
Postgresql存儲過程中的連接字符使用的是"||"
四,控制結構
使用LOOP,EXIT,CONTINUE,WHILE, 和FOR 語句,可以控制PL/pgSQL 函數重復一系列命令。需要使用的時候請查詳細資料。
五,異常捕獲

EXCEPTION
WHEN 錯誤碼(如:STRING_DATA_RIGHT_TRUNCATION:字串數據右邊被截斷) THEN
        /**后臺打印錯誤信息*/
        RAISE NOTICE '錯吳信息';

或者錯誤碼為UNDEFINED_TABLE時可以執行創建表的操作然后在入數據。

存儲過程的示例

例子1

/**
批量插入一批數據,經緯度字段值要滿足中國地理位置上的經緯度范圍;
注:時間不能指定為同一時間,否則會掃描全表,導致性能低下。下列腳本未考慮時間的分段,采用的一個時間點。
*/
create or replace function intobatch() returns integer as
$body$
declare
    skyid integer;
    lot float;
         lat float;
         sex varchar;
         level integer;
         ctime int :=1325404914;
         num integer :=0;
         total integer :=0;
    begin
                   lot='73.6666666';
                   lat='3.8666666';
                   FOR skyid IN 404499817 ..404953416 loop
                             if(lot > 135.0416666) then
             lot=73.6666666;
                             end if;
                        if(lat > 53.5500000) then
             lat=3.8666666;
                             end if;
                             if(skyid%2 <> 0) then
                                                        sex='1';
                                                        level=0;
                             else
                                                        sex='2';
                                                        level=1;
                             end if;
 
                            INSERT INTO user_last_location(user_id,app_id,lonlat,sex,accurate_level,lonlat_point,create_time)
 
VALUES(skyid,2934,ST_GeomFromText('POINT('||lot||' '||lat||')',4326),sex,level,POINT(lot,lat),to_timestamp(ctime));
                           
                            lot=lot+0.1;
                            lat=lat+0.1;
                            skyid=skyid+1;
                           
        end loop;        
                   return skyid;  
    end
$body$
languageplpgsql;
 
SELECT *from intobatch();

例子2:此例子是審計的生產環境的例子

-- 存儲過程
CREATE OR REPLACE FUNCTION audit_event_partition_trigger()
RETURNS TRIGGER AS $$
DECLARE date_text TEXT;
DECLARE end_date_text TEXT;
DECLARE insert_statement TEXT;
BEGIN
    SELECT to_char(NEW.time_event, 'YYYY_MM_DD') INTO date_text;
    SELECT to_char(NEW.time_event + INTERVAL '1 DAY', 'YYYY_MM_DD') INTO end_date_text;
    insert_statement := 'INSERT INTO audit_event_' || date_text || ' VALUES($1.*)';
    EXECUTE insert_statement USING NEW;
    RETURN NULL;
    EXCEPTION
    WHEN UNDEFINED_TABLE
    THEN
        EXECUTE 
            'CREATE TABLE IF NOT EXISTS audit_event_'  || date_text || '(CHECK ( time_event >= ''' || date_text || ''' AND  time_event < '''|| end_date_text ||''' )) INHERITS (audit_event)';
        RAISE NOTICE 'CREATE NON-EXISTANT TABLE audit_event_%', date_text;
        EXECUTE
            'CREATE INDEX audit_event_' ||  date_text || '_event_msg_body ON audit_event_'  || date_text || ' USING gin(event_msg_body gin_trgm_ops); ' 
            || 'CREATE INDEX audit_event_' || date_text || '_gid ON audit_event_'  || date_text || ' USING btree(gid);' 
            || 'CREATE INDEX audit_event_' || date_text || '_id ON audit_event_'  || date_text || ' USING btree(id);' 
            || 'CREATE INDEX audit_event_' || date_text || '_module ON audit_event_'  || date_text || ' USING btree(module);' 
            || 'CREATE INDEX audit_event_' || date_text || '_access_time ON audit_event_'  || date_text || ' USING btree(access_time);' 
            || 'CREATE INDEX audit_event_' || date_text || '_mid ON audit_event_'  || date_text || ' USING btree(mid);' 
            || 'CREATE INDEX audit_event_'  || date_text || '_time_event ON audit_event_' || date_text || ' USING btree(time_event);';
        EXECUTE insert_statement USING NEW;
    RETURN NULL;
END;
$$
LANGUAGE plpgsql

這個是由觸發器來引發執行的存儲過程:

DROP TRIGGER IF EXISTS auto_insert_into_audit_event_tbl_partiton ON audit_event;
CREATE TRIGGER  auto_insert_into_audit_event_tbl_partiton BEFORE INSERT OR UPDATE ON  audit_event  
FOR EACH ROW
EXECUTE PROCEDURE audit_event_partition_trigger()

reference
對存儲過程的一些理解
簡單通俗的解釋一下存儲過程是干什么的
分表分庫設計

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容