學至Tarena金牌講師,金色晨曦科技公司技術總監沙利穆課程筆記的綜合。
1. 什么是存儲過程和函數
將SQL語句放入一個集合里,然后直接調用存儲過程和函數來執行已經定義好的SQL語句,通過存儲過程和函數,可以避免開發人員重復編寫相同的SQL語句。
MYSQL存儲過程和函數是保存在服務器中,在服務器中存儲和執行,可以減少客戶端和服務器端之間數據傳輸的消耗。
存儲過程就是一組已經保存在數據庫中的語句,並且可以隨時地調用。
存儲過程允許標准組件式編程,存儲過程在被創建以后可以在程序中被多次調用而不必重新編寫該存儲過程的SQL語句,而且數據庫專業人員可隨時對存儲過程進行修改,但對應用程序源代碼毫無影響。因為應用程序源代碼只包含存儲過程的調用語句從而極大地提高了程序的可移植性。
1.1 什么時候要用到存儲過程(存儲過程的特點):
(1) 存儲過程是在服務器端運行的,它的執行速度比較快。
(2) 存儲過程執行一次后,就會駐留在高處緩沖存儲器中,在以后的操作中,只需要從高處緩沖存儲器中調用已經編譯好的二進制代碼來執行,這樣就能提高系統的性能和響應時間。
(3) 使用存儲過程,可以確保數據庫的安全,因為使用存儲過程可以完成數據庫的所有操作,因為可以把想要進行的操作都放入SQL語句中,然后通過編程的方式來控制對數據庫的訪問權限。
1.2 利用mysql的存儲過程比單獨執行mysql的優勢在哪里?好處有什么?
計算機上調用Transaction-SQL 編寫的一段程序原因在於存儲過程具有以下優點
1 存儲過程允許標准組件式編程
存儲過程在被創建以后可以在程序中被多次調用,而不必重新編寫該存儲過程的SQL語句,而且數據庫專業人員可隨時對存儲過程進行修改,但對應用程序源代碼毫無影響,因
為應用程序源代碼只包含存儲過程的調用語句,從而極大地提高了程序的可移植性
2 存儲過程能夠實現較快的執行速度
如果某一操作包含大量的Transaction-SQL 代碼,或分別被多次執行那么存儲過程要比批處理的執行速度快很多。因為存儲過程是預編譯的,在首次運行一個存儲過程時,查詢優化器對其進行分析優化,並給出最終被存在系統表中的執行計划,而批處理的Transaction-SQL 語句在每次運行時都要進行編譯和優化,因此速度相對要慢一些
3 存儲過程能夠減少網絡流量
對於同一個針對數據數據庫對象的操作,如查詢、修改,如果這一操作所涉及到的Transaction-SQL 語句被組織成一存儲過程,那么當在客戶計算機上調用該存儲過程時,網絡中傳送的只是該調用語句,否則將是多條SQL 語句從而大大增加了網絡流量降低網絡負載
4 存儲過程可被作為一種安全機制來充分利用
系統管理員通過對執行某一存儲過程的權限,進行限制從而能夠實現對相應的數據訪問權限的限制,避免非授權用戶對數據的訪問,保證數據的安全。
2. 創建存儲過程:
CREATE PROCEDURE sp_name
([proc_parameter[,…]])
[characteristic]
Routine_body
說明:
Sp_name:存儲過程名稱,自定義,盡量起一個有意義的名稱
([proc_parameter[,…]]):需要接收或者輸出的參數
[characteristic]:特性
Routine_body:要執行的代碼,寫在BEGIN和END中,BEGIN和END類似於函數體的{}
3.存儲過程的參數:
3.1 輸入輸出的參數
輸入輸出的參數 參數的名稱 參數的類型
3.1.1輸入輸出的參數包括:
IN:輸入,把外界的數據傳遞到存儲過程當中
OUT:輸出,把存儲過程的運算結果傳遞到外界
INOUT:輸入輸出,既可以把外界的數據傳遞到存儲過程當中,又可以把存儲過程的運算結果傳遞到外界
3.1.2參數的類型
可以是MYSQL數據庫中的任意類型
3.1.3 特性
LANGUAGE SQL:默認的,說明Routine_body部分是由SQL語句注冊,即數據庫默認的語言
DETERMINISTIC:指明存儲過程執行的結果是確定的,每次執行存儲過程的時候,相同的輸入會得到相同的輸出。
NOT DETERMINISTIC:指明存儲過程執行的結果不是確定的,每次執行存儲過程的時候,相同的輸入會得到不同的輸出,默認情況下,結果是非確定的。
子查詢使用SQL語句的限制:
CONTAINS SQL:表示子程序中可以包含SQL語句,但不包含讀或寫數據。默認情況下使用該限制。
NO SQL:不包含SQL語句
READS SQL DATA:包含查詢數據的語句
MODIFIES SQL DATA:包含寫數據的語句
--
SQL SECURITY DEFINER / INVOKER:誰有權限來執行這個存儲過程,DEFINER(默認)表示只有定義者自己可以執行,INVOKER表示調用者可以執行。
3.1.4 注釋
COMMENT ‘string’
‘string’:注釋信息,可以在創建存儲過程的時候指定注釋。
4. 改變存儲過程默認的定界符
通過DELIMITER來改變
例子:
DELIMITER //
CREATE PROCEDURE sp_demo1()
BEGIN
SELECT * FROM users2;
END
//
DELIMITER ;.
Query OK, 0 rows affected (0.28 sec)
說明:因為存儲過程也包含了很多SQL語句,而這些SQL語句也都是以分號結尾的,為了避免定界符的沖突,所以使用DELINITER來改變定界符
注意:
(1)DELIMITER與定界符之間,一定要有一個空格,否則設置將無效。
(2)要注意每次創建存儲過程結束后,要將定界符恢復為分號,這是一個好的習慣。
5.創建帶參數的存儲過程:
DELIMITER //
CREATE PROCEDURE age_from_user2(IN user_id INT,OUT user_age INT)
READS SQL DATA
BEGIN
SELECT age INTO user_age FROM user
WHERE id=user_id;
END
//
DELIMITER ;.
說明:
(1) INT表示參數的返回值,或者說是參數的數據類型
(2)INTO 參數名:表示將SQL語句執行的結果賦給INTO后面的參數中
創建IN參數的存儲過程的例子:
查詢訂貨量大於外界所傳遞進來的參數p_in的訂單數據
DELIMITER //
CREATE PROCEDURE proc2(IN p_in INT)
BEGIN
SELECT * FROM `order` WHERE onum>p_in;
END
//
DELIMITER ;
創建帶有OUT輸出參數的例子:
DELIMITER //
CREATE PROCEDURE proc3(OUT p_out INT )
BEGIN
SELECT count(*) INTO p_out FROM custom;
END
//
DELIMITER ;
說明:INTO 參數名:表示將count(*)的結果賦給INTO后面的參數中
創建帶有INOUT輸入輸出參數的存儲過程:
DELIMITER //
CREATE PROCEDURE proc4(INOUT p_io INT)
BEGIN
SET p_io=5;
END
//
DELIMITER ;
6.創建存儲函數
存儲過程與存儲函數本質上是相同的,都屬於存儲程序,也就是保存在數據庫當中的程序,用的時候,都可以隨時調用。
6.1 存儲過程與存儲函數的區別
(1)存儲過程可以指定IN、OUT參數,存儲函數不需要指定輸入輸出參數,存儲函數所有的參數都屬於IN參數。
(2)存儲函數可以通過RETURN語句將運算的結果返回,但是存儲過程不允許調用RETURN語句,存儲過程可以通過調用OUT參數,將運算的結果返回給外界。
6.2 創建存儲函數
CREATE FUNCTION func_name ([func_parameter[…]])
RETURNS type
[characteristic…]
Routine_body
說明:
RETURN type:表示返回值的類型
[characteristic…]:表示函數的特性,與存儲過程的特性一致
例子:
DELIMITER //
CREATE FUNCTION username_from_user(user_id INT)
RETURNS VARCHAR(20)
BEGIN
RETURN (SELECT username FROM users2 WHERE id=user_id);
END
//
DELIMITER ;
說明:
RETURNS 是指定返回值的數據類型
RETURN() 是將返回結果反饋給外界。
7.調用存儲過程
CALL sp_name ([parameter[,…]])
注意:
(1) 調用的時候,一定要有執行的權限
(2) 調用之后,系統執行存儲過程的語句,然后將輸出結果返回
例子:
CALL sp_demo1();
7.1調用有參數的存儲過程:
先創建一個存儲過程:
DELIMITER //
CREATE PROCEDURE age_from_user2(IN user_id INT,OUT user_age INT)
READS SQL DATA
BEGIN
SELECT age INTO user_age FROM user
WHERE id=user_id;
END
//
DELIMITER ;
然后調用這個存儲過程,注意,變量用@變量名稱
CALL age_from_user2(120,@use_age);
查看變量的值:
mysql> SELECT @use_age;
+----------+
| @use_age |
+----------+
| 22 |
+----------+
1 row in set (0.00 sec)
7.2調用有IN參數的存儲過程:
創建IN參數的存儲過程的例子:
查詢訂貨量大於外界所傳遞進來的參數p_in的訂單數據
DELIMITER //
CREATE PROCEDURE proc2(IN p_in INT)
BEGIN
SELECT * FROM `order` WHERE onum>p_in;
END
//
DELIMITER ;
調用有IN參數的存儲過程:
SET @num=10;
CALL proc2(@num);
7.3帶有OUT參數的存儲過程的調用
創建帶有OUT輸出參數的例子:
DELIMITER //
CREATE PROCEDURE proc3(OUT p_out INT )
BEGIN
SELECT count(*) INTO p_out FROM custom;
END
//
DELIMITER ;
說明:INTO 參數名:表示將count(*)的結果賦給INTO后面的參數中
調用存儲過程:
mysql> SET @amount=0;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL proc3(@amount);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT @amount;
+---------+
| @amount |
+---------+
| 4 |
+---------+
1 row in set (0.00 sec)
7.4 調用有INOUT參數的存儲過程
創建帶有INOUT輸入輸出參數的存儲過程:
DELIMITER //
CREATE PROCEDURE proc4(INOUT p_io INT)
BEGIN
SET p_io=5;
END
//
DELIMITER ;
調用有INOUT參數的存儲過程:
mysql> SET @num=-1;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL proc4(@num);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @num;
+------+
| @num |
+------+
| 5 |
+------+
1 row in set (0.00 sec)
8.調用存儲函數
調用存儲函數與調用系統函數的格式是一樣的。
SELECT func_name([parameter[,…]]);
例子:
創建存儲函數:
DELIMITER //
CREATE FUNCTION username_from_user(user_id INT)
RETURNS VARCHAR(20)
BEGIN
RETURN (SELECT username FROM users2 WHERE id=user_id);
END
//
DELIMITER ;
調用存儲函數:
mysql> SELECT username_from_user(120);
+-------------------------------------+
| username_from_user(120) |
+-------------------------------------+
| nihao |
+-------------------------------------+
1 row in set (0.05 sec)
調用存儲函數例子:
先創建存儲函數
DELIMITER //
CREATE FUNCTION func1(id CHAR)
RETURNS VARCHAR(10)
BEGIN
RETURN(SELECT cname FROM custom WHERE cid=id);
END
//
DELIMITER ;
調用存儲函數:
mysql> SET @id='110002';
Query OK, 0 rows affected (0.00 sec)
mysql> SET @name='';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT func1(@id) INTO @name;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT @name;
+--------------------------------------------+
| @name |
+--------------------------------------------+
| 湖北眾合糧油工業有限公司 |
+--------------------------------------------+
1 row in set (0.00 sec)
9. 查看已創建好的存儲過程和函數:
查看存儲過程:SHOW PROCEDURE STATUS LIKE ‘sp_name’;
查看存儲函數:SHOW FUNCTION STATUS LIKE ‘func_name’;
查看全部存儲過程:SHOW PROCEDURE STATUS \G
查看全部存儲函數:SHOW FUNCTION STATUS \G
注意:‘sp_name’和‘func_name’一定要加引號。
例:
SHOW PROCEDURE STATUS LIKE 'age_from_user3';
SHOW PROCEDURE STATUS LIKE 'age_from_user3'\G; (按行輸出)
*************************** 1. row ***************************
Db: homework (在哪個數據庫下)
Name: age_from_user3 (存儲過程的名字)
Type: PROCEDURE (存儲過程的類型)
Definer: root@localhost (存儲過程的定義者)
Modified: 2014-11-13 20:48:11 (存儲過程的修改時間)
Created: 2014-11-13 20:48:11 (存儲過程的創建時間)
Security_type: DEFINER (安全類型,誰可以執行這個存儲過程)
Comment: (注釋)
character_set_client: gbk (客戶端的字符集)
collation_connection: gbk_chinese_ci (校驗字符集)
Database Collation: utf8_bin (數據庫的字符集)
1 row in set (0.00 sec)
查看存儲函數:
SHOW FUNCTION STATUS LIKE 'username_from_user';
SHOW FUNCTION STATUS LIKE 'username_from_user'\G;
*************************** 1. row ***************************
Db: homework
Name: username_from_user
Type: FUNCTION
Definer: root@localhost
Modified: 2014-11-13 20:40:10
Created: 2014-11-13 20:40:10
Security_type: DEFINER
Comment:
character_set_client: gbk
collation_connection: gbk_chinese_ci
Database Collation: utf8_bin
1 row in set (0.00 sec)
10.查看存儲過程和存儲函數的定義
SHOW CREATE PROCEDURE sp_name;
SHOW CREATE FUNCTION func_name;
例子:
SHOW CREATE PROCEDURE age_from_user3\G;
*************************** 1. row ***************************
Procedure: age_from_user3
sql_mode: NO_ENGINE_SUBSTITUTION (SQL類型)
Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `age_from_user3`(IN user_id INT,OUT user_age INT)
READS SQL DATA
BEGIN
SELECT age INTO user_age FROM users2
WHERE id=user_id;
END
character_set_client: gbk
collation_connection: gbk_chinese_ci
Database Collation: utf8_bin
1 row in set (0.00 sec)
查看函數的創建例子:
mysql> SHOW CREATE FUNCTION username_from_user\G;
*************************** 1. row ***************************
Function: username_from_user
sql_mode: NO_ENGINE_SUBSTITUTION
Create Function: CREATE DEFINER=`root`@`localhost` FUNCTION `username_from_user`(user_id INT) RETURNS varchar(20) CHARSET utf8 COLLATE utf8_bin
BEGIN
RETURN (SELECT username FROM users2 WHERE id=user_id);
END
character_set_client: gbk
collation_connection: gbk_chinese_ci
Database Collation: utf8_bin
1 row in set (0.00 sec)
11. 查看數據庫information_schema中的存儲過程和函數
數據庫information_schema保存了所有的存儲過程和存儲函數,均保存在了ROUTINES表中。
(1) 切換數據庫:mysql> USE information_schema;
(2) 查看ROUTINES表:mysql> SELECT * FROM ROUTINES\G
或者使用:SELECT * FROM information_schema.routines \G
可以根據查詢結果的選項內容,進行條件查詢:
SELECT * FROM information_schema.routines WHERE routine_type='FUNCTION' \G
12.修改存儲過程和函數的屬性
我們需要明白這里的修改不是修改其中的SQL語句,而是修改它的安全性以及數據訪問。我們也可以通過客戶端工具進行查看和修改。
ALTER PROCEDURE sp_name [COMMENT ‘string’];
ALTER FUNCTION func_name [COMMENT ‘string’];
修改存儲過程:
mysql> ALTER PROCEDURE sp_demo1 COMMENT 'THIS IS A TEST';
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW PROCEDURE STATUS LIKE 'sp_demo1'\G;
修改存儲函數:
ALTER FUNCTION username_from_user COMMENT'THIS IS A TEST OF FUCTION';
mysql> SHOW FUNCTION STATUS LIKE 'username_from_user'\G;
12.1 存儲過程的安全性
安全類型有兩個:
DEFINER:定義者,定義這個存儲過程的人可以執行它,默認
INVOKER:調用者,調用這個存儲過程的人可以執行它
SQL 數據訪問選項有4個:
CONTAINS SQL:存儲過程或者函數包含SQL語句
NO SQL: 存儲過程或者函數不包含SQL語句
READS SQL DATA: 存儲過程或者函數的SQL語句是讀數據庫的數據
MODIFIES SQL DATA: 存儲過程或者函數的SQL語句是修改數據庫的數據
12.2 修改存儲過程和函數的安全性
修改之前先查看一下屬性值:
SELECT * FROM information_schema.routines \G
然后使用ALTER 語句修改存儲過程proc2的安全類型和數據訪問選項:
ALTER PROCEDURE proc2
MODIFIES SQL DATA
SQL SECURITY INVOKER;
SHOW PROCEDURE STATUS LIKE 'proc2'\G
SELECT * FROM information_schema.routines \G
13. 刪除存儲過程及存儲函數
DROP PROCEDURE sp_name;
DROP FUNCTION sp_name;
當刪除不存在的存儲過程或者存儲函數的時候,會顯示報錯:
mysql> DROP PROCEDURE sp_demo1;
ERROR 1305 (42000): PROCEDURE homework.sp_demo1 does not exist
如果想屏蔽錯誤,以警告的形式提示,可以使用:
DROP PROCEDURE IF EXISTS sp_name;
DROP FUNCTION IF EXISTS sp_name;
例子:
mysql> DROP PROCEDURE IF EXISTS sp_name;
Query OK, 0 rows affected, 1 warning (0.00 sec)
查看警告的內容:SHOW WARNINGS;
mysql> SHOW WARNINGS;
+-------+------+-------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-------------------------------------------------------------------------+
| Note | 1305 | PROCEDURE homework.sp_name does not exist |
+-------+------+-------------------------------------------------------------------------+
1 row in set (0.01 sec)
刪除存儲過程例子:
mysql> DROP PROCEDURE sp_demo1;
Query OK, 0 rows affected (0.05 sec)
mysql> SHOW CREATE PROCEDURE sp_demo1;
ERROR 1305 (42000): PROCEDURE sp_demo1 does not exist
刪除存儲函數例子:
DROP FUNCTION username_from_user;