一、概述
存儲過程可以理解為一段 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)
BEGINDECLARE executedTime INT ;SET executedTime = loopTime;while executedTime > 0 DOINSERT 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`()
BEGINDECLARE i INT;DECLARE tableName VARCHAR(30);DECLARE sqlText text;SET i = 0;WHILE i < 10 DOSET 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 stmtFROM@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 ifcase 的語法格式
首先是第一種寫法:
case 表達式when 值 then 語句when 值 then 語句...[else 語句]
end case
然后是第二種寫法:
casewhen 表達式 then 語句when 表達式 then 語句....[else 語句]
end caseloop 循環 語法格式為:
[標號:] loop循環語句
end loop [標號]while 語法
while a>100 do循環語句
End whileRepeat ? ? ? ?//游標SQL語句1UNTIL 條件表達式
END Repeat;LoopSQL語句所有的條件判斷和跳出需要自己實現
End loopleave 語句用來從標注的流程構造中退出,它通常和 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)
BEGINDECLARE executedTime INT ;SET executedTime = loopTime;while executedTime > 0 DOINSERT 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));*/
?
七、總結
存儲過程只做了解即可,事實上很多公司都是禁止使用存儲過程的,主要是因為一旦使用存儲過程,新人接手將會非常困難,并且難以調試和擴展,而且沒有可移植性。
何況存儲過程能夠解決的問題,一般程序代碼也是可以解決的,因此在非必要情況下,還是使用代碼去實現,而不是考慮去用存儲過程。
?
原文鏈接:https://www.haveyb.com/article/61