參考網站
這篇文章是以Postgres SQL的語法為主,其他RDB的語法略有不同
什麼是Store Procedure
預存程序(Stored Procedure)是一種資料庫對象,它是一個預編譯的SQL程式碼,可以在資料庫中存儲並重複使用。它通常由一系列SQL語句和編程邏輯組成,可以接受輸入參數、返回輸出參數和結果集。存儲過程通常由數據庫管理員或開發人員編寫和維護,並且可以在數據庫中執行。
存儲過程有以下幾個優點:
- 提高了數據庫性能:存儲過程是預編譯的,一旦編譯就會存儲在數據庫中,執行速度快。此外,存儲過程可以減少客戶端與服務器之間的通信,從而降低網絡延遲,提高數據庫性能。
- 提高了數據安全性:存儲過程可以在數據庫層面上實現安全控制,可以限制某些用戶或角色的操作權限,從而保證數據的安全性。
- 代碼復用:存儲過程可以被多個應用程序或者腳本重複調用,從而實現代碼復用,減少重複編寫代碼的時間和工作量。
- 事務處理:存儲過程可以封裝事務處理邏輯,當多個SQL語句需要在一個事務中執行時,可以使用存儲過程將它們封裝在一個事務中,從而保證了數據的一致性和完整性。
- 簡化了複雜的操作:存儲過程可以將多個SQL語句組合成一個操作,從而簡化了複雜的操作,使得應用程序更易於開發和維護。
存儲過程可以實現很多功能,比如:
- 數據庫的備份和還原
- 數據庫的複製和同步
- 數據庫的日誌記錄和監控
- 數據庫的查詢優化
- 數據庫的數據加密和解密
- 數據庫的數據清洗和轉換
- 數據庫的業務邏輯實現
- 數據庫的存儲和檢索過程中的錯誤處理
- 數據庫的報表生成和匯出等。
Procedure與Function之差異
在PostgreSQL中,function和procedure都是儲存運算邏輯的物件,它們在設計目的和使用方式上略有不同。
Function主要是用於執行一個特定的計算或邏輯,返回一個單一的值或一個結果集,並且可以在SELECT語句、WHERE語句等中使用。Function可以返回一個或多個結果集,但只能返回一個值作為輸出,因此它被稱為有返回值的Function。
Procedure主要用於執行一連串的操作,不返回結果集,通常是用來執行複雜的業務邏輯,如事務處理、資料庫管理等。Procedure可以執行INSERT、UPDATE、DELETE等數據操作,但是不能作為SELECT語句的一部分使用。Procedure不返回任何值或結果集,因此它被稱為無返回值的Procedure。
在設計時,需要根據實際需求選擇使用哪種物件。如果需要執行一個特定的計算或邏輯,並返回一個結果集,則使用Function。如果需要執行一連串的操作並且不需要返回結果集,則使用Procedure。
更直接的不同是,Procedure並沒有定義它自身的回傳值,連Returns void都沒有,而Function是有回傳值的,即使是Returns Void也是有回傳值
創建procedure的語法
1
2
3
4
5
6
7
| create or replace function test()
LANGUAGE plpgsql
AS $$
BEGIN
END
$$;
|
若加入RETURNS VOID則會回報錯誤
創建Function的語法
1
2
3
4
5
6
7
| create or replace function test()
LANGUAGE plpgsql
AS $$
BEGIN
END
$$;
|
若不指定回傳值,則會報錯誤function result type must be specified
定界符$$ (delimiter)
在SQL中,兩個美元符號($$)用來定義一個區塊或一個標識符(identifier)。
例如,可以使用$$
來.定義一個存儲過程或函數的主體。在$$之間的所有內容都被視為存儲過程或函數的主體,可以包含SQL語句、流程控制語句等。
另外,$$
還可以用來定義一個標識符,例如,可以使用$$
來定義一個變量或一個標識符的名稱。在$$
之間的所有內容都被視為標識符的名稱,可以包含字母、數字和下劃線等字符。使用$$
定義標識符時,可以幫助區分保留字和自定義的標識符,以及避免標識符中包含空格等特殊字符所帶來的問題。
1
2
3
4
5
6
7
8
| CREATE FUNCTION myfunc(x INT, y INT) RETURNS INT AS $$
BEGIN
RETURN x + y;
END;
$$ LANGUAGE plpgsql;
-- 在這個例子中,$$ 用來定義函數的主體。CREATE FUNCTION 指令創建了一個名為 myfunc 的函數,它接受兩個整數參數 x 和 y,返回它們的和。 RETURN 指令用於返回函數的值。
-- 在這裡,$ 用作定界符,它定義了函數的主體。如果不使用定界符,則在函數主體中使用分號 ; 可能會導致錯誤。使用定界符可以幫助區分不同的 SQL 區塊,從而更好地組織和管理 SQL 代碼。
|
使用Terminal操作Postgres SQL
有兩種方式,一種是用psql的Shell,另一種則是用powershell
使用psql的Shell
打開psql
輸入相應的資訊
使用powerShell
首先先下載Postgres SQL,接著在把環境變數加入至電腦中,路徑大概會如下
1
| C:\Program Files\PostgreSQL\14\bin
|
即可在Termianl中操作postgres SQL
如以下指令
查看當前Psql有什麼Database
連結進入Database
1
2
3
4
5
6
| psql [dbname]
psql -U [role] [dbname] # 以 postgres 的 role 登入 postgres 的 database
psql -U postgres # 使用 postgres 的 role 登入 的 postgres 這個 database
psql -U aaronchen minicare_demo # 以 aaronchen 的 role 登入 minicare_demo 這個 database
psql -U postgres -d minicare_demo
|
1
2
| psql -h localhost -p 5432 -U postgres school
## psql -h localhost -p 5432 -U <your_username> <your_database>
|
(與之對應的Server設定)
連結成功後的畫面
可以輸入
來查看該database底下的table
在 psql 中,\
符號是一個特殊的字元,稱為 “psql 內部命令前綴”。當您在 psql 終端機中輸入以 \
開頭的命令時,psql 將解釋這個命令,並執行對應的操作。
以下是幾個常用的 psql 內部命令:
\c
: 切換到另一個數據庫\dt
: 列出當前數據庫中的所有表格\d table_name
: 查看指定表格的結構\q
: 退出 psql 終端機\! clear
: 清空psql 終端機畫面
當您想要使用 psql 內部命令時,必須在命令前加上 \
符號。例如,要列出當前數據庫中的所有表格,您可以輸入 \dt
。
如果您想要輸入以 \
開頭的普通文本,而不是 psql 內部命令,則必須在 \
前面再加上一個 \
符號。例如,如果您想要在 SQL 陳述句中使用反斜線(\
)字元,則必須將其寫成 \\
。
請注意,在某些情況下,psql 內部命令前綴 \
可能會和 SQL 語句的關鍵字混淆。例如,CREATE TABLE
陳述句中的 TABLE
關鍵字後面如果不加引號可能會被 psql 認為是一個內部命令。為避免這種情況,建議在使用關鍵字時加上引號,例如 "TABLE"
。\
基本上這個terminal就是我們的Query Tool,所以是可以在這邊做CRUD的,如下
常用的psql 指令
\q
- 退出 psql
命令行界面。\c <database_name>
- 連接到指定的數據庫。\dt
- 列出當前數據庫中的所有表格。\d <table_name>
- 列出指定表格的詳細信息,包括列名、類型和約束等。\du
- 列出所有用戶帳號的詳細信息,包括用戶名、權限和群組等。\dp
- 列出所有表格的權限設置。\timing
- 啟用或禁用查詢執行時間的顯示。\set
- 列出或設置 psql
的配置選項。\i <file_path>
- 從指定的檔案載入 SQL 命令。\e
- 打開外部編輯器編輯當前緩衝區中的 SQL 命令。\! clear
: 清空psql 終端機畫面\dt
: 查看所有表格\df
: 查看資料庫中的所有函數以及Stored Procedure\ef
: 編輯現有的函數,可能你寫錯了還是啥的 ☆修改完後,存檔退出記事本,進到terminal要加上 \g
才會把這個修改go
出去,不然你怎麼改都是沒有用ㄉ,血與淚的教訓Q_Q\sf
: 查看更詳細的函數內容,s stand for show
dt, du, df的d代表Describe的縮寫,用以顯示資料庫的資料
Stored Procedure的實戰
首先創造表格
1
2
3
4
5
| CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE
);
|
插入資料
1
2
3
4
5
| INSERT INTO users (name, email)
VALUES ('John Doe', 'john.doe@example.com'),
('Jane Doe', 'jane.doe@example.com'),
('Bob Smith', 'bob.smith@example.com');
|
返回值為空的Function
先創建一個無關緊要的stored procedure來感受一下它的作用吧,我們創造一個function,用來返回今天的日期,語法如下
1
2
3
4
5
6
7
8
9
10
| CREATE OR REPLACE FUNCTION -- 創建stored procedure的起手式。
get_current_date() -- 函式名稱
RETURNS DATE -- 該函式的返回值,若未指定則為Return Null
AS -- 用以分明函數聲明與函數體,AS上面的是函數聲明,AS下面的是函數體
$$ -- 定界符,參照上方說明
BEGIN
RETURN CURRENT_DATE; -- 函數體
END;
$$ -- 定界符,參照上方說明
LANGUAGE plpgsql; -- 不加這個就是默認使用SQL語言,只能使用一些比較簡單的語法,加了這個之後就可以作流程控制、循環、異常處理等等...
|
接著呼叫它
1
| SELECT get_current_date();
|
回傳特定資料的function
該function會返回參數值的使用者的相關資訊
1
2
3
4
5
6
7
| CREATE OR REPLACE FUNCTION get_user_info(user_id INTEGER)
RETURNS TABLE (name VARCHAR(255), email VARCHAR(255)) AS
$$
BEGIN
RETURN QUERY SELECT users.name, users.email FROM users WHERE id = user_id;
END;
$$ LANGUAGE plpgsql;
|
更新資料的function
1
2
3
4
5
6
7
8
| CREATE OR REPLACE FUNCTION public.update_user_info(user_id integer, name character varying, email character varying)
RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE users AS u SET name = $2, email = $3 WHERE u.id = $1;
END;
$$
|
變數宣告
:=
來進行變數宣告,=
在postgresSQL比較像是用來比較
1
2
3
4
5
6
7
8
9
10
11
12
| CREATE OR REPLACE FUNCTION my_function(param1 integer, param2 integer)
RETURNS integer AS $$
DECLARE
var1 integer := 0;
var2 integer := 0;
BEGIN
var1 := param1 + 1;
var2 := param2 + 2;
RETURN var1 * var2;
END;
$$ LANGUAGE plpgsql;
|
WHILE 語法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| CREATE OR REPLACE PROCEDURE public.factorial(IN n INT, OUT result BIGINT)
LANGUAGE plpgsql
AS $$
DECLARE
i INT := 1;
BEGIN
result := 1;
WHILE i <= n LOOP
result := result * i;
i := i + 1;
END LOOP;
END;
$$;
|
IF語法
1
2
3
4
5
6
7
8
9
10
11
12
| CREATE OR REPLACE PROCEDURE example_if_proc (a INT)
AS
BEGIN
IF a < 0 THEN
DBMS_OUTPUT.PUT_LINE('a is negative');
ELSIF a = 0 THEN
DBMS_OUTPUT.PUT_LINE('a is zero');
ELSE
DBMS_OUTPUT.PUT_LINE('a is positive');
END IF;
END;
|
將SELECT出來的值儲存在一變數中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| CREATE OR REPLACE PROCEDURE public.get_customer_info(
IN customer_id INTEGER,
OUT customer_name VARCHAR(50),
OUT customer_email VARCHAR(50)
)
LANGUAGE plpgsql
AS $$
BEGIN
SELECT name, email INTO customer_name, customer_email
FROM customers
WHERE id = customer_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Customer not found';
END IF;
END;
$$;
|
將資訊打印出來(print)
1
2
3
4
5
6
7
8
9
| CREATE OR REPLACE procedure sayHello()
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
BEGIN
RAISE NOTICE 'hello word:';
END;
$$
|