PostgreSQL的存儲過程簡單入門
一、存儲過程結構:
Create or replace function 過程名(參數名 參數類型,…..) returns 返回值類型 as $body$ //聲明變量 Declare 變量名變量類型; 如: flag Boolean; 變量賦值方式(變量名類型 :=值;) 如: str text :=值; / str text; str :=值; Begin 函數體; return 變量名; //存儲過程中的返回語句 End; $body$ Language plpgsql;
二、變量類型 :
除了postgresql內置的變量類型外,常用的還有 RECORD ,表示一條記錄。
整數數據類型:
浮點數據類型:
(浮點數據也可以再細分,分為提供通用功能的浮點值和固定精度的數字)
注:
存儲float和real類型的數據的行為非常相似,但是numeric列的行為有點不同。Numeric類型不是存儲接近的數,而是在小數后面進行后超出固定長度的部分進行四舍五入。如果我們存儲太大的數據到其中,INSERT將失敗。還要注意float和real也會對數字四舍五入;例如123.456789被四舍五入為123.457。
時間數據類型:
特殊數據類型:
注: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 把控制交給塊結束后的下一個語句。
例如:
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;
---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;