一、概述
存儲過程可以理解爲一段 SQL 語句的集合(相當於 PHP 中的一個函數方法,去實現業務邏輯),它們被事先編譯好並且存儲在數據庫中。
調用存儲過程與直接執行 SQL 語句的效果是相同的,但是存儲過程的一個好處是處理邏輯都封裝在數據庫端。
當我們調用存儲過程的時候,我們不需要了解其中的處理邏輯,一旦處理邏輯發生變化,只需要修改存儲過程即可,對調用它的程 序完全無影響。
調用存儲過程和函數可以簡化應用開發人員的很多工作,減少數據在數據庫和應用服務器之間的傳輸,減少了和腳本語言的交互以及帶寬,可以提高數據處理的效率。
二、存儲過程結構
create procedure 【存儲過程名(參數列表)】
begin
【存儲過程體】
end
call 存儲過程名(參數列表)
三、使用示例
實例1、新建一張數據表,並向這張數據表中添加 100 萬條記錄。
(1)新建數據表
CREATE TABLE `test_table` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`loop` int(10) unsigned NOT NULL DEFAULT '0',
`name` varchar(256) NOT NULL DEFAULT '',
`pen_name` varchar(256) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
(2)新建存儲過程
DROP PROCEDURE IF EXISTS insert_many_rows;
CREATE PROCEDURE insert_many_rows (IN loopTime INT)
BEGIN
DECLARE executedTime INT ;
SET executedTime = loopTime;
while executedTime > 0 DO
INSERT INTO test_table(NULL, 0, 'sss', 'kkk');
SET executedTime = executedTime - 1;
END WHILE;
END;
(3)呼叫存儲過程
CALL insert_many_rows(1000000);
結果應該是新建的數據表中已經有了 100 萬條記錄。
實例2:通過存儲過程創建10個數據表,分別爲test_table_0 ~ test_table_9
(1)創建存儲過程
DROP PROCEDURE IF EXISTS create_test_tables;
CREATE PROCEDURE `create_test_tables`()
BEGIN
DECLARE i INT;
DECLARE tableName VARCHAR(30);
DECLARE sqlText text;
SET i = 0;
WHILE i < 10 DO
SET tableName = CONCAT('test_table_' , i);
SET sqlText = CONCAT('CREATE TABLE ', tableName , '(
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`loop` int(10) unsigned NOT NULL DEFAULT ''0'',
`name` varchar(256) NOT NULL DEFAULT '''',
`pen_name` varchar(256) NOT NULL DEFAULT '''',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2001001 DEFAULT CHARSET=utf8;');
SET @sqlText = sqlText;
PREPARE stmt
FROM
@sqlText;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET i = i + 1;
END WHILE;
END
(2)呼叫存儲過程
CALL create_test_tables();
四、分析
1、存儲過程的參數類型:
(1)IN 表示只是用來輸入。
(2)OUT 表示只是用來輸出。
(3)INOUT 可以用來輸入,也可以用作輸出。
2、存儲過程中的變量聲明
通過 DECLARE 來聲明一個局部變量,該變量的作用域只是 begin....end 塊中。
變量的聲明可以添加默認值,比如:
DECLARE executedTime INT DEFAULT 0;
3、流程控制語句語法
if 的語法格式爲:
if 條件表達式 then 語句
[elseif 條件表達式 then 語句] ....
[else 語句]
end if
case 的語法格式
首先是第一種寫法:
case 表達式
when 值 then 語句
when 值 then 語句
...
[else 語句]
end case
然後是第二種寫法:
case
when 表達式 then 語句
when 表達式 then 語句
....
[else 語句]
end case
loop 循環 語法格式爲:
[標號:] loop
循環語句
end loop [標號]
while 語法
while a>100 do
循環語句
End while
Repeat //遊標
SQL語句1
UNTIL 條件表達式
END Repeat;
Loop
SQL語句
所有的條件判斷和跳出需要自己實現
End loop
leave 語句用來從標註的流程構造中退出,它通常和 begin...end 或循環一起使用
leave 標號;
聲明語句結束符,可以自定義:
DELIMITER [符合]
delimiter $$
4、存儲過程中的數據類型
數值類型:Int、float、double、decimal
日期類型:timestamp、date、year
字符串:char、varchar、text
五、存儲過程優缺點
1、優點:
(1)執行速度快。因爲我們的每個 SQL 語句都需要經過編譯,然後再運行。但是存儲過程都是直接編譯好了之後,直接運行即可。
(2)減少網絡流量,我們傳輸一個存儲過程比我們傳輸大量的 SQL 語句的開銷要小得多。
(3)提高系統安全性,因爲存儲過程可以使用權限控制,而且參數化的存儲過程可以有效地防止 SQL 注入攻擊。保證了其安全性。
(4)耦合性降低。當我們的表結構發生了調整或變動之後,我們可以修改相應的存儲過程,我們的應用程序在一定程度上需要改動的地方就較小了。
(5)重用性強,因爲我們寫好一個存儲過程之後,再次調用它只需要一個名稱即可,也就是”一次編寫,隨處調用”,而且使用存儲過程也可以讓程序的模塊化加強。
2、缺點:
(1)可移植性差。因爲存儲過程是和數據庫綁定的,如果我們要更換數據庫之類的操作,可能很多地方都需要改動。
(2)修改不方便。因爲對於存儲過程而言,我們並不能特別有效的調試,它的一些 bug 可能發現的更晚一些,增加了應用的危險性。
(3)優勢不明顯和贅餘功能。對於小型 web 應用來說,如果我們使用語句緩存,發現編譯 SQL 的開銷並不大,但是使用存儲過程卻需要檢查權限一類的開銷,這些贅餘功能也會在一定程度上拖累性能。
六、PHP 中使用存儲過程
PHP 中也是可以使用存儲過程的,存儲過程的使用也很簡單。只要將存儲過程的創建語句和call語句分別執行就可以了。
這裏使用最簡單的 pdo 調用方式,如果在框架中,爲了保持代碼的美觀,請使用框架自帶的查詢執行語句。```
<?php
declare(strict_types = 1);
// 注意:創建存儲過程和call存儲過程要分開執行,創建存儲過程之後,將創建存儲過程部分註釋掉,然後打開call存儲過程代碼執行
// 連接 pdo
$dsn = "mysql:dbname=test;host=127.0.0.1";
$pdo = new PDO($dsn,'root','123456');
# ------------------------------- 創建存儲過程 --------------------------
// 創建存儲過程語句賦值到變量
$sql = 'DROP PROCEDURE IF EXISTS insert_many_rows_2;
CREATE PROCEDURE insert_many_rows_2 (IN loopTime INT)
BEGIN
DECLARE executedTime INT ;
SET executedTime = loopTime;
while executedTime > 0 DO
INSERT INTO test_table(NULL, 0, \'sss\', \'kkk\');
SET executedTime = executedTime - 1;
END WHILE;
END;';
// 執行
$stmt = $pdo->query($sql);
var_dump($stmt->fetchAll(2));
#---------------------- call 存儲過程 --------------------------
/*$callSql = 'CALL insert_many_rows(1000000);';
$stmt = $pdo->query($callSql);
var_dump($stmt->fetchAll(2));*/
七、總結
存儲過程只做瞭解即可,事實上很多公司都是禁止使用存儲過程的,主要是因爲一旦使用存儲過程,新人接手將會非常困難,並且難以調試和擴展,而且沒有可移植性。
何況存儲過程能夠解決的問題,一般程序代碼也是可以解決的,因此在非必要情況下,還是使用代碼去實現,而不是考慮去用存儲過程。