PostgreSQL的存儲過程簡單入門



PostgreSQL的存儲過程簡單入門

一、存儲過程結構:

 Create or replace function 過程名(參數名 參數類型,…..) returns 返回值類型 as

                   $body$



                            //聲明變量

                            Declare

                            變量名變量類型;

                            如:

                            flag Boolean;



                            變量賦值方式(變量名類型 :=值;)

                            如:

                            str  text :=值; / str  text;  str :=值;



                            Begin

                                     函數體;



                             return 變量名; //存儲過程中的返回語句



                            End;

                   $body$

         Language plpgsql;
 
 

二、變量類型 :

除了postgresql內置的變量類型外,常用的還有 RECORD ,表示一條記錄。

整數數據類型:

image

浮點數據類型:

(浮點數據也可以再細分,分為提供通用功能的浮點值和固定精度的數字)

image

注:

存儲float和real類型的數據的行為非常相似,但是numeric列的行為有點不同。Numeric類型不是存儲接近的數,而是在小數后面進行后超出固定長度的部分進行四舍五入。如果我們存儲太大的數據到其中,INSERT將失敗。還要注意float和real也會對數字四舍五入;例如123.456789被四舍五入為123.457。

時間數據類型:

image

特殊數據類型:

image

注:PostgreSQL也允許你使用SQL命令CREATE TYPE在數據庫中建立你自己的類型。這通常不需要,而且在一定程度上,它是PostgreSQL獨有的


數組

通常,一個數組需要通過使用一個附加表實現。但是,數組的能力有時候很有用。建立數組的方法有兩種:傳統的PostgreSQL的方法和SQL99標准的方法。


PostgreSQL樣式的數組

要將一個表的列定義為數組,你可以簡單地在類型后面添加[];不需要定義元素的個數。即使定義了個數,也不會強制要求存儲的個數。
Eg:

test=> CREATE TABLE empworkday (

test(>     refcode char(5),

test(>     workdays int[]

test(> );

往數組列中插入值:

test=> INSERT INTO empworkday VALUES(‘val01′,‘{0,1,0,1,1,1,1}’);

test=> INSERT INTO empworkday VALUES(‘val02′,‘{0,1,1,1,1,0,1}’);


SQL99樣式的數字

在SQL99標准中,必須指出元素的個數。

Eg:

test=> CREATE TABLE empworkday (

test(>     refcode char(5),

test(>     workdays int array[7]

test(> );

test=> INSERT INTO empworkday VALUES(‘val01′,‘{0,1,0,1,1,1,1}’);

test=> INSERT INTO empworkday VALUES(‘val02′,‘{0,1,1,1,1,0,1}’);



三、連接字符:

         Postgresql存儲過程中的連接字符不再是“+”,而是使用“||”。


四、  控制結構:

1、if 條件(五種形式)

IF ... THEN

IF ... THEN ... ELSE

IF ... THEN ... ELSE IF

IF ... THEN ... ELSIF ... THEN ... ELSE

IF ... THEN ... ELSEIF ... THEN ... ELSE(注:ELSEIF 是ELSIF 的別名)

2、循環

 

1)、LOOP
[ <<label>> ]
 LOOP
     statements
 END LOOP [ label ];
 --LOOP 定義一個無條件的循環,無限循環, 直到由EXIT或者RETURN語句終止。可選的label 可以由EXIT 和CONTINUE 語句使用, 用於在嵌套循環中聲明應該應用於哪一層循環。
2)、EXIT

    EXIT [ label ] [ WHEN expression ];
     如果沒有給出label, 那么退出最內層的循環,然后執行跟在 END LOOP 后面的語句。 如果給出 label, 那么它必須是當前或者更高層的嵌套循環塊或者語句塊的標簽。然后該命名塊或者循環就會終止,而控制落到對應循環/塊的 END 語句后面的語句上。
如果聲明了WHEN,循環退出只有在expression 為真的時候才發生, 否則控制會落到EXIT 后面的語句上。

EXIT 可以用於在所有的循環類型中,它並不僅僅限制於在無條件循環中使用。在和 BEGIN 塊一起使用的時候,EXIT 把控制交給塊結束后的下一個語句。

例如:

Loop  --循環

Ifthen  --條件判斷

Exit ;-- 條件成立,則退出循環。

End if;

End loop;
3)、CONTINUE

CONTINUE [label ] [ WHENexpression ];

如果沒有給出 label,那么就開始最內層的循環的下一次執行。也就是說,控制傳遞回給循環控制表達式(如果有),然后重新計算循環體。 如果出現了label,它聲明即將繼續執行的循環的標簽。

如果聲明了 WHEN,那么循環的下一次執行只有在expression 為真的情況下才進行。否則,控制傳遞給CONTINUE 后面的語句。

CONTINUE 可以用於所有類型的循環; 它並不僅僅限於無條件循環。

例如:

LOOP
     --一些計算
    EXIT WHEN count > 100;
     CONTINUE WHEN count < 50;
     ---一些在count 數值在 [50 .. 100] 里面時候的計算
END LOOP;

 

4)、WHILE

     [ <<label>> ]
     WHILE expression LOOP
         statements
    END LOOP [ label ];
--只要條件表達式為真,WHILE語句就會不停在一系列語句上進行循環. 條件是在每次進入循環體的時候檢查的.
例如:
WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
     --- 可以在這里做些計算
END LOOP;
 WHILE NOT BOOLEAN_expression LOOP
     --- 可以在這里做些計算
END LOOP;

 

5)、FOR(整數變種)


  

  [ <<label>> ]
     FOR name IN [ REVERSE ] expression .. expression LOOP
         statements
     END LOOP [ labal ];
--這種形式的FOR對一定范圍的整數數值進行迭代的循環。變量name 會自動定義為integer類型並且只在循環里存在。給出范圍上下界的兩個表達式在進入循環的時候計算一次。 迭代步進值總是為 1,但如果聲明了REVERSE就是 -1。

--一些整數FOR循環的例子∶

FOR i IN 1..10 LOOP 表示1循環到10
  -- 這里可以放一些表達式
    RAISE NOTICE 'i IS %', i;
 END LOOP;

 FOR i IN REVERSE 10..1 LOOP
   --  這里可以放一些表達式
END LOOP;
---如果下界大於上界(或者是在 REVERSE 情況下是小於),那么循環體將完全不被執行。而且不會拋出任何錯誤。


 

3、異常捕獲

        EXCEPTION

WHEN 錯誤碼(如:STRING_DATA_RIGHT_TRUNCATION:字串數據右邊被截斷) THEN

        /**后台打印錯誤信息*/

        RAISE NOTICE '錯吳信息';

 


五、示例代碼:

/**

批量插入一批數據,經緯度字段值要滿足中國地理位置上的經緯度范圍;

注:時間不能指定為同一時間,否則會掃描全表,導致性能低下。下列腳本未考慮時間的分段,采用的一個時間點。

*/

create orreplace 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();



---postgresql 游標,函數,存儲 過程使用例子
CREATE OR REPLACE FUNCTION cursor_demo()
  RETURNS refcursor AS  --返回一個游標
$BODY$
declare  --定義變量及游標
    unbound_refcursor refcursor;  --游標
    t_accid varchar;    --變量
        t_accid2 int;    --變量

begin  --函數開始
    open unbound_refcursor for execute 'select name from cities_bak';  --打開游標 並注入要搜索的字段的記錄
    loop  --開始循環
        fetch unbound_refcursor into t_accid;  --將游標指定的值賦值給變量

        if found then  --任意的邏輯
            raise notice '%-',t_accid;
        else
            exit;
        end if;
    end loop;  --結束循環
    close unbound_refcursor;  --關閉游標
    raise notice 'the end of msg...';  --打印消息
    return unbound_refcursor; --為函數返回一個游標
exception when others then  --拋出異常
    raise exception 'error-----(%)',sqlerrm;--字符“%”是后面要顯示的數據的占位符
end;  --結束
$BODY$

  LANGUAGE plpgsql;  --規定語言
select cursor_demo(); --調用





postgresql 使用游標筆記

  游標介紹:游標是一種從表中檢索數據並進行操作的靈活手段,游標主要用在服務器上,處理由客戶端發送給服務端的sql語句,或是批處理、存儲過程、觸發器中的數據處理請求。

       游標的優點在於它允許應用程序對查詢語句select 返回的行結果集中每一行進行相同或不同的操作,而不是一次對整個結果集進行同一種操作;它還提供對基於游標位置而對表中數據進行刪除或更新的能力。缺點是處理大數據量時,效率低下,占用內存大。一般來說,能使用其他方式處理數據時,最好不要使用游標,除非是當你使用while循環,子查詢,臨時表,表變量,自建函數或其他方式都無法處理某種操作的時候,再考慮使用游標。

        游標是系統為用戶開設的一個數據緩沖區,存放SQL語句的執行結果。游標的一個常見用途就是保存查詢結果,以便以后使用。游標的結果集是由SELECT語句產生,如果處理過程需要重復使用一個記錄集,那么創建一次游標而重復使用若干次,比重復查詢數據庫要快的多。

        PostgreSQL游標可以封裝查詢並對其中每一行記錄進行單獨處理。當我們想對大量結果集進行分批處理時可以使用游標,因為一次性處理可能造成內存溢出。另外我們可以定義函數返回游標類型變量,這是函數返回大數據集的有效方式,函數調用者根據返回游標對結果進行處理。

  游標使用順序:聲明游標   >   打開游標   >  使用游標   >   關閉游標 。

 

  先展示一個游標的示例,以下get_film_titles(integer)函數接受代表電影發行年份的參數。在函數內部,我們查詢所有發行年份等於傳遞給該函數的發行年份的電影。我們使用光標在各行之間循環,並連接標題和標題包含ful 單詞的電影發行年份。

復制代碼
CREATE OR REPLACE FUNCTION get_film_titles(p_year INTEGER) RETURNS text AS $$ -- 聲明游標
DECLARE titles TEXT DEFAULT ''; rec_film RECORD; cur_films CURSOR(p_year INTEGER) FOR SELECT * FROM film WHERE release_year = p_year; BEGIN
   -- 打開游標
   OPEN cur_films(p_year); LOOP -- 獲取記錄放入film
      FETCH cur_films INTO rec_film; -- exit when no more row to fetch
      EXIT WHEN NOT FOUND; -- 構建輸出
      IF rec_film.title LIKE '%ful%' THEN titles := titles || ',' || rec_film.title || ':' || rec_film.release_year; END IF; END LOOP; -- 關閉游標
   CLOSE cur_films; RETURN titles; END; $$ LANGUAGE plpgsql; SELECT get_film_titles(2006); --返回結果 ,Grosse Wonderful:2006,Day Unfaithful:2006,Reap Unfaithful:2006,Unfaithful Kill:2006,Wonderful Drop:2006
復制代碼

 

 

一、聲明游標

  PostgreSQL聲明游標有兩種方法,一種是使用特殊類型REFCURSOR聲明游標變量,另一種是聲明和查詢綁定使用。

-- 第一種方式
DECLARE my_cursor REFCURSOR; -- 第二種方式
cursor_name [ [NO] SCROLL ] CURSOR [( name datatype, name data type, ...)] FOR query;

  首先,為光標指定一個變量名。接着指定是否可以使用向后滾動光標SCROLL,如果使用NO SCROLL,則光標無法向后滾動。然后,在CURSOR關鍵字后面加上一個逗號分隔的參數列表(name datatype),這些參數定義了查詢的參數。打開游標時,這些參數將被值替換。最后,可以在FOR關鍵字之后指定查詢,使用任何有效的SELECT語句。

  示例:

DECLARE cur_films CURSOR FOR SELECT * FROM film; cur_films2 CURSOR (year integer) FOR SELECT * FROM film WHERE release_year = year; -- 該cur_films 包含film表所有行。 -- 本cur_films2 包含film表特定發行年份的記錄。

 

 

二、打開游標

  PostgreSQL提供了用於打開未綁定和綁定的游標的語法。

  1.打開未綁定的游標

OPEN unbound_cursor_variable [ [ NO ] SCROLL ] FOR query;
復制代碼
-- 由於聲明時未綁定的游標變量未綁定到任何查詢,因此在打開它時必須指定查詢。請參見以下示例:
OPEN my_cursor FOR SELECT * FROM city WHERE counter = p_country; -- PostgreSQL允許我們打開游標並將其綁定到動態查詢。語法如下:
OPEN unbound_cursor_variable[ [ NO ] SCROLL ] FOR EXECUTE query_string [USING expression [, ... ] ]; -- 在下面的示例中,我們構建一個動態查詢,該動態查詢根據一個sort_field參數對行進行排序,並打開執行該動態查詢的游標。
query := 'SELECT * FROM city ORDER BY $1'; OPEN cur_city FOR EXECUTE query USING sort_field;
復制代碼

 

  2.打開綁定的游標

 因為綁定游標在聲明時已經綁定到查詢,所以當我們打開它時,只需要在必要時將參數傳遞給查詢。
OPEN cursor_variable[ (name:=value,name:=value,...)];
-- 在下面的示例中,我們打開了綁定游標,cur_films並cur_films2在上面聲明了該游標:
OPEN cur_films; OPEN cur_films2(year:=2005);

 

 

三、使用游標

  使用FETCH,MOVE,UPDATE或DELETE語句操作游標。

  1.獲取下一行

FETCH [ direction { FROM | IN } ] cursor_variable INTO target_variable;
該FETCH語句從游標中獲取下一行,並為其分配一個target_variable,它可以是記錄,行變量或逗號分隔的變量列表。如果找不到更多行,則將target_variable其設置為NULL(s)。

  如果不顯示指定方向,方向缺省為NEXT。可以有下面值:

  • NEXT
  • LAST
  • PRIOR
  • FIRST
  • ABSOLUTE count
  • RELATIVE count
  • FORWARD
  • BACKWARD

  請注意,FORWARD和BACKWARD方向僅適用於用SCROLL option 聲明的游標。

  示例:

FETCH cur_films INTO row_film; FETCH LAST FROM row_film INTO title, release_year;

 

  2.移動光標

MOVE [ direction { FROM | IN } ] cursor_variable;

  如果只想移動游標而不檢索任何行,則使用該MOVE語句。方向接受與FETCH語句相同的值。

MOVE cur_films2; MOVE LAST FROM cur_films; MOVE RELATIVE -1 FROM cur_films; MOVE FORWARD 3 FROM cur_films;

 

  3.刪除和更新行

  使用DELETE WHERE CURRENT OF或UPDATE WHERE CURRENT OF語句刪除或更新游標標識的行。

復制代碼
UPDATE table_name SET column = value, ... WHERE CURRENT OF cursor_variable; DELETE FROM table_name WHERE CURRENT OF cursor_variable; 
示例:
UPDATE film SET release_year = p_year WHERE CURRENT OF cur_films;
復制代碼

 

 

四、關閉游標

使用CLOSE關閉打開的游標,CLOSE語句釋放資源或釋放游標變量,以允許使用該OPEN語句再次打開它。

CLOSE cursor_variable;

 

 

五、其他

復制代碼
-- 臨時表返回結果例子
BEGIN; DO $$ DECLARE temp_geometry st_geometry; geometry_record RECORD; cur_geometry CURSOR FOR SELECT shape as shape FROM mainbasin; BEGIN
        OPEN cur_geometry; FETCH cur_geometry INTO temp_geometry; LOOP FETCH cur_geometry INTO geometry_record; EXIT WHEN NOT FOUND; temp_geometry := st_union(temp_geometry,geometry_record.shape); END LOOP; CLOSE cur_geometry; DROP TABLE IF EXISTS temp_table; CREATE TEMP TABLE temp_table AS 
        SELECT st_envelope(temp_geometry) shape; END; $$; COMMIT; SELECT st_astext(shape) FROM temp_table; 

復制代碼

 




PostgreSQL function里面調用function

1. 調用無參無返回值的function


create or replace function func01()returns void as $$
begin
	raise notice ' from func01(): hello PG';
end ;
$$language plpgsql;



create or replace function func02() returns void as $$
begin
	perform  func01();
end;
$$language plpgsql;

----運行:
select  func02();

--注意:   from func01(): hello PG
CONTEXT:  SQL statement "SELECT func01()"
在PERFORM的第3行的PL/pgSQL函數"func02"

--查詢總耗時: 14 ms.
--檢索到 1 行。




2. 調用無參有返回值的function


create or replace function func03()returns integer as $$
begin
	return 1;
end ;
$$language plpgsql;

create or replace function func02() returns void as $$
begin
	perform  func03();
end;
$$language plpgsql;



執行select fun02()的時候是沒有任何返回值的,因為perform已經將結果丟棄。

將perform更改為select into:

create or replace function func02() returns void as $$
declare n int;
begin
	select into n func03();
	raise notice 'n: %',n;
end;
$$language plpgsql;


執行: 

select  func02();

注意:  n: 1

查詢總耗時: 12 ms.
檢索到 1 行。



3. 有參有返回值

create or replace function func04(n int)returns integer as $$
begin
	return n;
end ;
$$language plpgsql;


create or replace function func02() returns void as $$
declare n int;
begin
	n=func04(4);
	raise notice 'n: %',n;
end;
$$language plpgsql;



執行:

select  func02();

注意:  n: 4

查詢總耗時: 11 ms.
檢索到 1 行。



















PostgreSQL執行動態sql,應用在存儲過程



drop function if exists exe_dynamic_sql(bigint);
drop function if exists exe_dynamic_count(bigint);
--返回記錄集
create or replace function exe_dynamic_sql(ival bigint)
    returns table(objectid bigint,name varchar(128))
as $$
	declare
	begin
		return query execute 'select objectid,name from dictionarys where parentid=$1 order by parentid,sort' using $1;
	end;
$$ language plpgsql;

--賦值給變量
create or replace function exe_dynamic_count(ival bigint)
    returns bigint
as $$
	declare
		v_count bigint;
	begin
		execute 'select count(*) from dictionarys where parentid=$1' using $1 into v_count;
		return v_count;
	end;
$$ language plpgsql;
--測試
select * from exe_dynamic_sql(26);
select exe_dynamic_count(26);




要點:

returns table(objectid bigint,name varchar(128)),定義返回的字段和類型
using $1執行時使用過程參數;
准備一個語句用於執行,這個就比較重要了,查詢參數綁定,開發利器
有朋友抱怨同一sql時快時慢,這是因為值在表中的占比不同,占比小的值就可以使用索引,值占比超過5%里sql就很慢了.此時就可以用下面的sql調式sql,可以根據不同的值來觀察執行計划.



--getDictionarys僅在當前會話下有效
prepare getDictionarys (bigint) as
    select objectid,name from dictionarys where parentid=$1 order by parentid,sort;

explain (analyze,verbose,costs,buffers,timing)
execute getDictionarys(24);

explain (analyze,verbose,costs,buffers,timing)
execute getDictionarys(25);

explain (analyze,verbose,costs,buffers,timing)
execute getDictionarys(26);

--釋放指定的預備語句
deallocate getDictionarys;
--釋放所有預備語句
deallocate all;




Postgresql 存儲過程--sql語句的where條件的拼接操作



--1、存儲過程返回一個表

CREATE OR REPLACE FUNCTION 存儲過程名(

    IN 參數1text,
    IN 參數2text,
    IN 參數3text)
  RETURNS TABLE(v_id integer, v_n text, v_me text, v_sid integer, v_sno integer, v_sname text) AS
$BODY$


DECLARE sql text;

BEGIN
       sql:='SELECT a.sid,a.no,a.name,b.sid,b.no,b.name 
FROM  表名 a
INNER JOIN 表名 b ON a.station_id = b.sid'; 


       IF 參數1= '' AND 參數2= '' AND 參數3= '' THEN
RETURN QUERY EXECUTE sql;

        ELSEIF 參數1= '' AND 參數2 = '' THEN
          sql:= sql || ' WHERE b.no = '''||in_stationNo||'''';
          RETURN QUERY EXECUTE sql;


        ELSEIF 參數2 = '' AND 參數3= '' THEN
           sql:= sql || '   WHERE a.name LIKE ''%'||參數1||'%''';
           RETURN QUERY EXECUTE sql;

        ELSEIF 參數1= '' AND 參數3= '' THEN
           sql:= sql || '   WHERE a.no LiKE ''%'||參數2||'%''';
           RETURN QUERY EXECUTE sql;


        ELSEIF 參數1= '' THEN
           sql:= sql || '   WHERE a.name LIKE ''%'||參數2||'%''AND b.no = '''||參數3||'''';
            RETURN QUERY EXECUTE sql;


        ELSEIF 參數2= '' THEN
            sql:= sql || '   WHERE a.no LIKE ''%'||參數1||'%''AND b.no = '''||參數3||'''';
            RETURN QUERY EXECUTE sql;


        ELSEIF 參數3= '' THEN
            sql:= sql || '   WHERE a.name LIKE ''%'||參數2||'%''AND a.no LIKE ''%'||參數1||'%''';
            RETURN QUERY EXECUTE sql;
        ELSE
             sql:= sql || '   WHERE a.name LIKE ''%'||參數2||'%''AND a.no LIKE ''%'||參數1||'%''AND b.no = '''||參數3||'''';
            RETURN QUERY EXECUTE sql;
              END IF;
END
$BODY$
  LANGUAGE plpgsql VOLATILE;
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM