MySQL - 存儲過程

一、概述

  存儲過程可以理解爲一段 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));*/

 

七、總結

存儲過程只做瞭解即可,事實上很多公司都是禁止使用存儲過程的,主要是因爲一旦使用存儲過程,新人接手將會非常困難,並且難以調試和擴展,而且沒有可移植性。

何況存儲過程能夠解決的問題,一般程序代碼也是可以解決的,因此在非必要情況下,還是使用代碼去實現,而不是考慮去用存儲過程。

 

原文鏈接:https://www.haveyb.com/article/61

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