PostgreSQL 存儲過程與函數


wangzhiqing999
PostgreSQL 存儲過程與函數
創建一個存儲過程PostgreSQL 好像沒有專門的 CREATE OR REPLACE PROCEDURE 全部都是 FUNCTION 的樣子。
對於沒有返回值的。 可以通過 RETURNS void  來實現。


要更新一個現有函數的定義,使用 CREATE OR REPLACE FUNCTION。 
我們不能用這個方法修改一個函數的名字或者參數類型(如果你這麼幹,那麼你就會創建一個新的,不同的函數)。 
同樣,CREATE OR REPLACE FUNCTION 也不會允許你修改一個現有函數的返回類型。 
要做這些事情,你必須刪除並重新創建函數。 
(如果使用 OUT 參數,那就意味着除了刪除函數,你不能修改任何 OUT 參數的類型或者名字。)


如果你刪除然後重建一個函數,新函數和舊的將是不同的實體;你就需要刪除現有引用了老函數的規則,視圖,觸發器等等。 
使用 CREATE OR REPLACE FUNCTION 可以在不破壞引用該函數的對象的前提下修改函數定義。

好像沒有 print 之類的語句。
只好 把結果寫入臨時表


Test=# CREATE TABLE test_helloworld(
Test(#   data  varchar(30)
Test(# );
CREATE TABLE
Test=#


請注意, 定義存儲過程內使用的變量, 需要定義在 BEGIN 之前, 需要加 DECLARE 關鍵字。
多個變量之間用分號分隔。


Test=# CREATE OR REPLACE FUNCTION HelloWorld() RETURNS void AS
Test-# $$
Test$# DECLARE
Test$#   testvalue1  VARCHAR(20);
Test$#   testvalue2  VARCHAR(20);
Test$# BEGIN
Test$#   testvalue1 := 'First Test! ';
Test$#   SELECT 'Second Test !' INTO testvalue2;
Test$#   INSERT INTO test_helloworld
Test$#     SELECT 'Hello World' ;
Test$#   INSERT INTO test_helloworld (data)
Test$#     VALUES (testvalue1 || testvalue2);
Test$# END;
Test$# $$
Test-# LANGUAGE plpgsql;
CREATE FUNCTION
Test=#
Test=# SELECT HelloWorld();
 helloworld
------------

(1 行記錄)


Test=# select * from test_helloworld;
           data
---------------------------
 Hello World
 First Test! Second Test !
(2 行記錄)


Test=#
修改存儲過程 


要更新一個現有函數的定義,使用 CREATE OR REPLACE FUNCTION。 
我們不能用這個方法修改一個函數的名字或者參數類型(如果你這麼幹,那麼你就會創建一個新的,不同的函數)。 
同樣,CREATE OR REPLACE FUNCTION 也不會允許你修改一個現有函數的返回類型。 
要做這些事情,你必須刪除並重新創建函數。 
(如果使用 OUT 參數,那就意味着除了刪除函數,你不能修改任何 OUT 參數的類型或者名字。)


如果你刪除然後重建一個函數,新函數和舊的將是不同的實體;你就需要刪除現有引用了老函數的規則,視圖,觸發器等等。 
使用 CREATE OR REPLACE FUNCTION 可以在不破壞引用該函數的對象的前提下修改函數定義。


具體代碼略.


需要注意的一點。

刪除函數的時候, 需要傳遞完整的參數列表, 僅僅指定一個  函數的名稱, 是無法刪除的。


例如:

Test=# drop FUNCTION HelloWorld;
錯誤:  語法錯誤 在 ";" 或附近的
第1行drop FUNCTION HelloWorld;
                             ^
Test=# drop FUNCTION HelloWorld();
DROP FUNCTION
Test=#


Test=# drop FUNCTION HelloWorld2;
錯誤:  語法錯誤 在 ";" 或附近的
第1行drop FUNCTION HelloWorld2;
                              ^


Test=# drop FUNCTION HelloWorld2();
錯誤:  函數 helloworld2() 不存在


Test=# drop FUNCTION HelloWorld2(varchar);
DROP FUNCTION
Test=#

參數定義 單個參數 

Test=# truncate table test_helloworld;
TRUNCATE TABLE


Test=# CREATE OR REPLACE FUNCTION HelloWorld1(vUserName VARCHAR) RETURNS void AS
Test-# $$
Test$# BEGIN
Test$#   INSERT INTO test_helloworld
Test$#     VALUES('Hello ' || vUserName);
Test$# END;
Test$# $$
Test-# LANGUAGE plpgsql;
CREATE FUNCTION
Test=#
Test=# SELECT HelloWorld1('ABC');
 helloworld1
-------------

(1 行記錄)


Test=# select * from test_helloworld;
   data
-----------
 Hello ABC
(1 行記錄)


函數參數的別名Test=# truncate table test_helloworld;
TRUNCATE TABLE


-- 請注意這裏: 定義參數的時候, 沒有定義參數名稱, 僅僅定義了參數的數據類型
-- 然後在 定義變量的位置, 通過  vUserName ALIAS FOR $1  來爲 第一個參數, 指定一個變量名稱, 叫做  vUserName
Test=# CREATE OR REPLACE FUNCTION HelloWorld2 (varchar) RETURNS void AS
Test-# $$
Test$# DECLARE
Test$#   vUserName ALIAS FOR $1;
Test$# BEGIN
Test$#   INSERT INTO test_helloworld
Test$#     VALUES('Hello ' || vUserName);
Test$# END;
Test$# $$
Test-# LANGUAGE plpgsql;
CREATE FUNCTION
Test=# SELECT HelloWorld2('XYZ');
 helloworld2
-------------

(1 行記錄)


Test=# select * from test_helloworld;
   data
-----------
 Hello XYZ
(1 行記錄)


某些情況下, 希望定義參數的時候, 數據類型,與某個表中的某一列的數據類型一樣。
這樣,將來萬一業務變化, 表的數據類型變化了,不需要修改存儲過程代碼。
定義的方式,是  表名.列名%TYPECREATE TABLE test_type (test_ID    INT,
  test_name  varchar(20)
);


Test=# CREATE OR REPLACE FUNCTION HelloWorld20 (
Test(#   p_user_name  test_type.test_name%TYPE Test(# ) RETURNS void ASTest-# $$
Test$# BEGIN
Test$#   INSERT INTO test_type VALUES(1, p_user_name);
Test$# END;
Test$# $$
Test-# LANGUAGE plpgsql;
注意:  類型關聯 test_type.test_name%TYPE 轉換爲 character varying
CREATE FUNCTION
Test=#

Test=# select HelloWorld20('Test');
 helloworld20
--------------

(1 行記錄)

Test=# select * from test_type;
 test_id | test_name
---------+-----------
       1 | Test
(1 行記錄)


參數定義- IN、OUT、IN OUT 

Test=# truncate table test_helloworld;
TRUNCATE TABLE

Test=# CREATE OR REPLACE FUNCTION HelloWorld3 (
Test(#   IN vUserName VARCHAR,
Test(#   OUT vOutValue VARCHAR
Test(# ) AS
Test-# $$
Test$# BEGIN
Test$#   INSERT INTO test_helloworld
Test$#     VALUES('Hello ' || vUserName);
Test$#   vOutValue := 'A';
Test$# END;
Test$# $$
Test-# LANGUAGE plpgsql;
CREATE FUNCTION
Test=#
Test=# SELECT HelloWorld3('ABC');
 helloworld3
-------------
 A
(1 行記錄)




Test=# select * from test_helloworld;
   data
-----------
 Hello ABC
(1 行記錄)


參數的默認值 

PostgreSQL  不直接支持 參數的默認值。
但是支持 重載。


Test=# TRUNCATE TABLE test_helloworld;
TRUNCATE TABLE


Test=# CREATE OR REPLACE FUNCTION HelloWorld3(
Test(#   p_user_name VARCHAR,
Test(#   p_val1 VARCHAR,
Test(#   p_val2 VARCHAR) RETURNS void AS
Test-# $$
Test$# BEGIN
Test$#   INSERT INTO test_helloworld (data)
Test$#     VALUES (p_user_name || p_val1 || p_val2);
Test$# END;
Test$# $$
Test-# LANGUAGE plpgsql;
CREATE FUNCTION
Test=#


Test=# CREATE OR REPLACE FUNCTION HelloWorld3(
Test(#   p_user_name VARCHAR,
Test(#   p_val1 VARCHAR) RETURNS void AS
Test-# $$
Test$# BEGIN
Test$#   PERFORM HelloWorld3(p_user_name, p_val1, ' XYZ');
Test$# END;
Test$# $$
Test-# LANGUAGE plpgsql;
CREATE FUNCTION
Test=#

Test=# CREATE OR REPLACE FUNCTION HelloWorld3(
Test(#   p_user_name VARCHAR) RETURNS void AS
Test-# $$
Test$# BEGIN
Test$#   PERFORM HelloWorld3(p_user_name, ' OPQ ');
Test$# END;
Test$# $$
Test-# LANGUAGE plpgsql;
CREATE FUNCTION
Test=#

Test=# SELECT HelloWorld3('ABC');
 helloworld3
-------------

(1 行記錄)

Test=# select * from test_helloworld;
     data
--------------
 ABC OPQ  XYZ
(1 行記錄)

Test=#

返回結果集 

簡單查詢的函數

請注意:
這裏最後寫的是  LANGUAGE SQL;  不是  LANGUAGE plpgsql;
因爲函數裏面, 沒有任何邏輯, 只有一條 SQL 語句.


CREATE OR REPLACE FUNCTION GetTestMain (int)  RETURNS test_main AS $$
  SELECT * FROM test_main WHERE id = $1;
$$ LANGUAGE SQL;


Test=# SELECT * FROM GetTestMain(1) AS t;
 id | value
----+-------
  1 | ONE
(1 行記錄)


Test=# CREATE OR REPLACE FUNCTION GetTestMain (int)  RETURNS test_main AS $$
Test$#   SELECT * FROM test_main WHERE id != $1;
Test$# $$ LANGUAGE SQL;
CREATE FUNCTION


Test=# SELECT * FROM GetTestMain(0) AS t;
 id | value
----+-------
  1 | ONE
(1 行記錄)


Test=# SELECT * FROM GetTestMain(1) AS t;
 id | value
----+-------
  2 | TWO
(1 行記錄)


請注意: 上面這種寫法, 如果查詢返回多行數據的情況下,這個函數僅僅會返回第一行。


Test=# CREATE OR REPLACE FUNCTION GetTestMain2(int) RETURNS setof test_main AS $$
Test$#   SELECT * FROM test_main WHERE id != $1;
Test$# $$ LANGUAGE SQL;
CREATE FUNCTION


Test=#
Test=# SELECT * FROM GetTestMain2(1) AS t;
 id | value
----+-------
  2 | TWO
  4 | FOUR
(2 行記錄)


通過定義  RETURNS setof ... 使得函數能過返回多行記錄.


假如業務邏輯比較複雜,無法簡單 SQL 處理的情況下


需要使用 RETURN NEXT ... 來把當前行數據,加入結果集.
使用 RETURN; 把整個結果集返回.


Test=# CREATE OR REPLACE FUNCTION GetTestMain3(int)
Test-# RETURNS SETOF test_main AS
Test-# $$
Test$# DECLARE
Test$#   v_test_main_data test_main%ROWTYPE;
Test$# BEGIN
Test$#   FOR v_test_main_data IN SELECT * FROM test_main LOOP
Test$#  IF v_test_main_data.id = $1 THEN
Test$#    -- 模擬一點邏輯操作.
Test$#    CONTINUE;
Test$#  END IF;
Test$#  -- 把當前行數據,加入結果集.
Test$#     RETURN NEXT v_test_main_data;
Test$#   END LOOP;
Test$#   -- 把整個結果集返回.
Test$#   RETURN;
Test$# END;
Test$# $$ LANGUAGE plpgsql;
CREATE FUNCTION


Test=# SELECT * FROM GetTestMain3(1) AS t;
 id | value
----+-------
  2 | TWO
  4 | FOUR
(2 行記錄)



普通返回的函數
Test=# CREATE OR REPLACE FUNCTION HelloWorld4() RETURNS varchar AS
Test-# $$
Test$# BEGIN
Test$#   RETURN 'Hello World!';
Test$# END;
Test$# $$
Test-# LANGUAGE plpgsql;
CREATE FUNCTION


Test=#
Test=# select HelloWorld4();
 helloworld4
--------------
 Hello World!
(1 行記錄)







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章