因為pgsql中沒有存儲過程和包,所以類似功能通過函數來實現
PostgreSQL的存儲過程簡單入門 http://blog.csdn.net/rachel_luo/article/details/8073458
存儲過程事物 http://www.php100.com/manual/PostgreSQL8/tutorial-transactions.html
PL/pgSQL - SQL存儲過程語言 https://wiki.postgresql.org/wiki/9.1%E7%AC%AC%E4%B8%89%E5%8D%81%E4%B9%9D%E7%AB%A0
postgreSQL存儲過程寫法示例http://blog.sina.com.cn/s/blog_448574810101f64u.html
結構
PL/pgSQL是一種塊結構的語言,比較方便的是用pgAdmin III新建Function,填入一些參數就可以了。基本上是這樣的:
- CREATE OR REPLACE FUNCTION 函數名(參數1,[整型 int4, 整型數組 _int4, ...])
- RETURNS 返回值類型 AS
- $BODY$
- DECLARE
- 變量聲明
- BEGIN
- 函數體
- END;
- $BODY$
- LANGUAGE ‘plpgsql’ VOLATILE;
變量類型
除了postgresql內置的變量類型外,常用的還有 RECORD ,表示一條記錄。
賦值
賦值和Pascal有點像:“變量 := 表達式;”
有些奇怪的是連接字符串的是“||”,比如 sql := ‘SELECT * FROM’ || table || ‘WHERE …’;
判斷
判斷又和VB有些像:
IF 條件 THEN
…
ELSEIF 條件 THEN
…
ELSE
…
END IF;
循環
循環有好幾種寫法:
WHILE expression LOOP
statements
END LOOP;
還有常用的一種是:(從1循環到9可以寫成FOR i IN 1..9 LOOP)
FOR name IN [ REVERSE ] expression .. expression LOOP
statements
END LOOP;
其他
還有幾個常用的函數:
SELECT INTO record …; 表示將select的結果賦給record變量(RECORD類型)
PERFORM query; 表示執行query並丟棄結果
EXECUTE sql; 表示執行sql語句,這條可以動態執行sql語句(特別是由參數傳入構造sql語句的時候特別有用)
參數:
傳遞給函數的參數都是用 $1,$2,等等這樣的標識符。有時候為了增強可讀性,我們可以為 $n 參數名聲明別名。然后通過這個別名或者數字標識符可以指向這個參數值。
有兩種方法創建一個別名。最好的方法是用CREATE FUNCTION命令給予這個參數一個名字,例如:
- CREATE FUNCTION sales_tax(subtotal real) RETURNS real AS $$
- BEGIN
- RETURN subtotal * 0.06;
- END;
- $$ LANGUAGE plpgsql;
另一個方法是,在PostgreSQL 8.0之前唯一的方法,明確的用別名進行聲明,用以下的語法進行聲明:
name ALIAS FOR $n;
這個風格的同一個例子看起來像下面這樣 :
- CREATE FUNCTION sales_tax(real) RETURNS real AS $$
- DECLARE
- subtotal ALIAS FOR $1;
- BEGIN
- RETURN subtotal * 0.06;
- END;
- $$ LANGUAGE plpgsql;
注意:這兩個例子不是完全一樣的。在第一種情況,subtotal可以用sales_tax.subtotal進行引用,但是在第二種情況下不能這么做。(如果我們給這個內部塊附加了一個標簽,subtotal能夠替代這個標簽)
一些更多的例子:
- CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$
- DECLARE
- v_string ALIAS FOR $1;
- index ALIAS FOR $2;
- BEGIN
- -- some computations using v_string and index here
- 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
當一個PL/pgSQL函數用輸出參數來進行聲明時,給予這個輸出參數$n名和一個任意的別名跟正常輸入參數是同樣的方法。即使這個輸出參數以NULL開始時也是一個有效的變量,它應該在函數的執行過程中被分配。這個參數最好的值將被返回。例如,這個sales-tax例子也可以用這種方法完成:
- CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$
- BEGIN
- tax := subtotal * 0.06;
- END;
- $$ LANGUAGE plpgsql;
注意:我們省略了RETURNS real---我們可以將它包括在內,但它是多余的。
當返回多個值的時候輸出參數將非常有用,一個簡單的例子是:
- 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中的討論,這將為這個函數的結果創建一個匿名的記錄類型。如果使用了RETURNS字句,那么必須給它指明RETURNS記錄。
另外一種方法聲明PL/pgSQL函數是用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;
這跟聲明一個或者多個OUT參數和制定RETURNS SETOF這些類型是同樣的方法。
當返回的PL/pgSQL函數的類型被聲明為一個多態類型(anyelement, anyarray, anynonarray, 或者anyenum),特殊參數$0將被創建。它的數據類型將實際的返回函數的類型,從實際的輸入類型返回(見Section 35.2.5)。這運行這個函數訪問這個實際的返回類型如Section 39.3.3顯示的那樣。$0初始值為空並且能夠被函數修改,如果需要,它可以用於保留返回值,雖然這不是必須的。$0也可以被給予一個別名。例如,這個函數能在任意一個有+操作符的數據類型上工作:
- 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
聲明一個或者多個多態類型的輸出參數也是同樣的效果。這種情況下這個特殊的$0參數將不會被用到,這個輸出參數本身也是同樣的作用,例如:
- CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement,
- OUT sum anyelement)
- AS $$
- BEGIN
- sum := v1 + v2 + v3;
- END;
- $$ LANGUAGE plpgsql;
39.3.2. 別名
newname ALIAS FOR oldname;
這個ALIAS語法比以前的章節中介紹的更加普通:你可以為任意一個變量聲明一個別名,不只是函數的參數。這實際的用途是用預定義的名字為變量定義不同的名字,如觸發器過程中的NEW或者OLD。 例子:
- DECLARE
- prior ALIAS FOR old;
- updated ALIAS FOR new;
因此,ALIAS使同樣的對象有兩種不同的方式命名,如果不限制的使用,將會變得混亂。這種方法最好只用於覆蓋預定義的名字。
最后,貼出解決上面這個問題的存儲過程吧:
- CREATE OR REPLACE FUNCTION message_deletes(ids "varchar", userid int8)
- RETURNS int4 AS
- $BODY$
- DECLARE
- r RECORD;
- del bool;
- num int4 := 0;
- sql "varchar";
- BEGIN
- sql := 'select id,receiveuserid,senduserid,senddelete,receivedelete from message where id in (' || ids || ')';
- FOR r IN EXECUTE sql LOOP
- del := false;
- IF r.receiveuserid=userid and r.senduserid=userid THEN
- del := true;
- ELSEIF r.receiveuserid=userid THEN
- IF r.senddelete=false THEN
- update message set receivedelete=true where id = r.id;
- ELSE
- del := true;
- END IF;
- ELSEIF r.senduserid=userid THEN
- IF r.receivedelete=false THEN
- update message set senddelete=true where id = r.id;
- ELSE
- del := true;
- END IF;
- END IF;
- IF del THEN
- delete from message where id = r.id;
- num := num + 1;
- END IF;
- END LOOP;
- return num;
- END;
- $BODY$
- LANGUAGE 'plpgsql' VOLATILE;
下面的例子是要調用一個存儲過程自動創建對應的一系列表:
- CREATE OR REPLACE FUNCTION create_table_for_client(id int)
- RETURNS integer AS
- $BODY$
- DECLARE
- num int4 := 0;
- sql "varchar";
- BEGIN
- sql := 'create table _' || id || '_company(id int, name text)';
- EXECUTE sql;
- sql := 'create table _' || id || '_employee(id int, name text)';EXECUTE sql;
- sql := 'create table _' || id || '_sale_bill(id int, name text)';EXECUTE sql;
- .......
- return num;
- END;
- $BODY$ LANGUAGE plpgsql VOLATILE
自動創建序列
第一個例子
- CREATE OR REPLACE FUNCTION auto_gen_seq() RETURNS bigint AS
- $BODY$
- DECLARE
- rd RECORD;
- num int4 := 0;
- sql "varchar";
- seq_sql varchar;
- BEGIN
- sql := 'SELECT tablename FROM pg_tables WHERE tablename NOT LIKE ''pg%'' AND tablename NOT LIKE ''sql_%'' ORDER BY tablename;';
- FOR rd IN EXECUTE sql LOOP
- seq_sql:='CREATE SEQUENCE SQ_'||rd.tablename||' START 1000000 CACHE 30;';
- BEGIN
- EXECUTE seq_sql;
- EXCEPTION
- WHEN TOO_MANY_ROWS THEN
- RAISE EXCEPTION 'employee % not unique', seq_sql;
- WHEN OTHERS THEN
- return -1;
- END;
- num := num + 1;
- END LOOP;
- return num;
- END;
- $BODY$
- LANGUAGE plpgsql VOLATILE NOT LEAKPROOF
- COST 100;
調用:
- select auto_gen_seq()
第二個例子
- -- Function: auto_gen_seq(character)
- -- DROP FUNCTION auto_gen_seq(character);
- CREATE OR REPLACE FUNCTION auto_gen_seq("tbName" character)
- RETURNS character varying AS
- $BODY$/*
- 調用示例:
- SELECT tablename as tableName,
- 'sq_'||REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(tablename, 'tb_am_', ''), 'tb_sm_', ''), 'tb_pm_', ''), 'tb_pc_', ''), 'tb_ps_', ''), 'rh_', ''), 'TB_', ''), 'RH_', '' ), 'tb_', '') AS sqName
- ,auto_gen_seq(tablename||'') as successFlag,current_date,current_time FROM pg_tables WHERE tablename NOT LIKE 'pg%' AND tablename NOT LIKE 'sql_%' ORDER BY successFlag,tablename;
- */
- DECLARE
- rd RECORD;
- seq_sql varchar;
- flag_str varchar;
- sq_name varchar;
- sq_datetime varchar;
- BEGIN
- seq_sql:='create table _sequence_table ( id SERIAL not null, code VARCHAR(200) null, increment_num INT8 null, minvalue_num INT8 null, maxvalue_num INT8 null, start_num INT8 null, cache_num INT8 null, cycle_flag VARCHAR(100) null,create_datetime timestamp without time zone,constraint PK__SEQUENCE_TABLE primary key (id) );CREATE UNIQUE INDEX INDEX__sequence_table ON _sequence_table (code);';
- BEGIN
- EXECUTE seq_sql;
- EXCEPTION
- WHEN OTHERS THEN
- flag_str:='失敗';
- END;
- --sq_name:=replace(replace($1, 'TB_', ''), 'RH_', '');
- --sq_name:=replace(replace(sq_name, 'tb_', ''), 'rh_', '');
- sq_name:='sq_'||REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE($1, 'tb_am_', ''), 'tb_sm_', ''), 'tb_pm_', ''), 'tb_pc_', ''), 'tb_ps_', ''), 'rh_', ''), 'TB_', ''), 'RH_', '' ), 'tb_', '');
- /*
- seq_sql:='drop SEQUENCE '||sq_name||';';
- BEGIN
- EXECUTE seq_sql;
- EXCEPTION
- WHEN OTHERS THEN
- flag_str:='失敗';
- END;
- */
- seq_sql:='CREATE SEQUENCE '||sq_name||' START 1000000 CACHE 30;';
- BEGIN
- EXECUTE seq_sql;
- EXCEPTION
- WHEN OTHERS THEN
- return '失敗,創建序列';
- END;
- sq_datetime:=to_timestamp(current_date||' '||current_time,'yyyy-mm-dd hh24:mi:ss') ;
- seq_sql:='INSERT INTO _sequence_table( code,increment_num,minvalue_num,start_num, cache_num,create_datetime) VALUES ( '''||sq_name||''',1,1000000,1000000, 30,'''||sq_datetime||''');';
- BEGIN
- EXECUTE seq_sql;
- EXCEPTION
- WHEN OTHERS THEN
- return '失敗,插入序列信息';
- END;
- return '成功';
- END;
- $BODY$
- LANGUAGE plpgsql VOLATILE STRICT
- COST 100;
- ALTER FUNCTION auto_gen_seq(character)
- OWNER TO postgres;
調用
- SELECT tablename as tableName,
- 'sq_'||REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(tablename, 'tb_am_', ''), 'tb_sm_', ''), 'tb_pm_', ''), 'tb_pc_', ''), 'tb_ps_', ''), 'rh_', ''), 'TB_', ''), 'RH_', '' ), 'tb_', '') AS sqName
- ,auto_gen_seq(tablename||'') as successFlag,current_date,current_time FROM pg_tables WHERE tablename NOT LIKE 'pg%' AND tablename NOT LIKE 'sql_%' ORDER BY successFlag,tablename;