存儲過程和函數是事先經過編譯並存儲在數據庫中的一段SQL語句的集合,存儲和和函數的區別在於函數必須有返回值,而存儲過程沒有,存儲過程的參數可以使用IN、OUT、INOUT類型,而函數的參數只能是IN類型。存儲過程再簡單點來說,就是為以后的使用而保存的一條或多條MySQL語句的集合。可將其視為批件,雖然它們的作用不僅限於批處理。在我看來, 存儲過程就是有業務邏輯和流程的集合, 可以在存儲過程中創建表,更新數據, 刪除等等。本次博客就來講一下存儲過程
存儲過程的操作
語法如下:
創建: CREATE PROCEDURE sp_name([proc_parameter[,...]]) [characteristic...] routine_body proc_parameter: [IN|OUT|INOUT] param_name type #type: Any valid MySQL data type characteristic: LANGUAGE SQL |[NOT] DETERMINISTIC|{CONTAINS SQL|NO SQL|READS SQL DATA|MODIFIES SQL DATA}|SQL SECURITY {DEFINAER|INVOKER}|COMMENT 'string' routine_body: Valid SQL procedure statement or statements 修改: ALTER PROCEDURE sp_name [characteristic...] characteristic: {CONTAINS SQL|NO SQL|READS SQL DATA|MODIFIES SQL DATA}|SQL SECURITY {DEFINAER|INVOKER}|COMMENT 'string' 調用: CALL sp_name([parameter[,...]]) 刪除: DROP PROCEDURE sp_name 查看: show PROCEDURE STATUS [like 'pattern'] SHOW CREATE PROCEDURE sp_name
MySQL的存儲過程和函數中允許包含DDL語句,也允許在存儲過程中執行提交或者回滾,但是存儲過程和函數不允許執行LOAD DATA INFILE語句,存儲過程和函數可以調用其他的過程或者函數。
插入小知識點@:
1.用戶變量:以"@"開始,形式為"@變量名" 用戶變量跟mysql客戶端是綁定的,設置的變量,只對當前用戶使用的客戶端生效。 2.全局變量:定義方式 set GLOBAL 變量名 或者 set @@global.變量名 對所有客戶端生效,只有具有super權限才可以設置全局變量。
存儲過程簡單來說,就是為以后的使用而保存的一條或多條MySQL語句的集合。可將其視為批件,雖然它們的作用不僅限於批處理。
在我看來, 存儲過程就是有業務邏輯和流程的集合, 可以在存儲過程中創建表,更新數據, 刪除等等。
為什么要使用存儲過程
- 通過把處理封裝在容易使用的單元中,簡化復雜的操作(正如前面例子所述)。
- 由於不要求反復建立一系列處理步驟,這保證了數據的完整性。如果所有開發人員和應用程序都使用同一(試驗和測試)存儲過程,則所使用的代碼都是相同的。這一點的延伸就是防止錯誤。需要執行的步驟越多,出錯的可能性就越大。防止錯誤保證了數據的一致性。
- 簡化對變動的管理。如果表名、列名或業務邏輯(或別的內容)有變化,只需要更改存儲過程的代碼。使用它的人員甚至不需要知道這些變化。
users表如下:
創建存儲過程,傳入性別(男或女),顯示對應性別的用戶id,返回對應性別的人數(我的是在mysql front中操作):
#DELIMITER $$ CREATE PROCEDURE user_procedure(IN sex VARCHAR(2) character set utf8,OUT num INT) BEGIN SELECT id FROM users WHERE gender=sex; SELECT FOUND_ROWS() INTO num; END #$$ #DELIMITER ;
如果大家用的navicat版本,應該改成是:
DELIMITER $$ CREATE PROCEDURE user_procedure(IN sex VARCHAR(2) character set utf8,OUT num INT) BEGIN SELECT id FROM users WHERE gender=sex; SELECT FOUND_ROWS() INTO num; END $$ DELIMITER ;
上面記得中文字符字段,一定要設置編碼:character set utf8,這里自己被坑了好久才覺悟過來...
調用
CALL user_procedure('女',@num); select @num;
定義條件和處理
條件的定義和處理可以用來定義在處理過程中遇到問題時相應的處理步驟。
語法如下:
條件定義: DECLARE condition_name CONDITION FOR condition_value condition_value: SQLSTATE [VALUE] sqlstate_value |mysql_error_code 條件處理: DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement handler_type: CONTINUE|EXIT|UNDO condition_value: SQLSTATE [VALUE] sqlstate_value |condition_name|SQLWARNING|NOT FOUND|SQLEXCEPTION|mysql_error_code
繼續用users表舉個例子吧!
現在有數據如下:
(1)當沒有進行條件處理的時候:
#delimiter $$ create procedure user_insert() begin set @x=1; insert into users(id,gender,name) values(1,'男','常貴'); set @x=2; insert into users(gender,name) values('女','大腳'); set @x=3; END #$$
上面的例子可以看出,當插入id=1,主鍵重復了,直接退出了,並沒有執行余下的語句,所以@x的值為1。
(2)可以對主鍵重復進行處理:
#delimiter $$ create procedure user_insert2() begin declare continue handler for sqlstate '23000' set @x2=1; set @x=1; insert into users(id,gender,name) values(3,'男','jack'); set @x=2; insert into users(id,gender,name) values(1,'男','mary'); set @x=3; end #$$ #delimiter ;
調用call user_insert2();
這次在調用存儲過程的時候,並沒有報錯,而是在遇到主鍵重復的時候,會安裝定義的continue去執行,所以繼續向下執行。
condition_value的值可以是通過declare定義的condition_name,可以是SQLSTATE的值或者mysql_error_code的值會在是SQLWARNING、NOT FOUND、SQLEXCEPTION,這個3個值是3種定義好的錯誤類別,分別代表不同的含義:
SQLWARNING:是對所有以01開頭的SQLSTATE代碼的速記
NOT FOUND是對所有以02開頭的SQLSTATE代碼的速記
SQLEXCEPTION是對所有沒有被SQLWARNING或者NOT FOUND捕獲的SQLSTATE代碼的速記。
以上的declare continue handler for sqlstate '23000' set @x2=1;也可以用以下幾種方式來寫:
#捕獲mysql-error-code declare continue handler for 1062 set @x2=1; #事先定義condition_name declare duplicatekey condition for sqlstate '23000'; declare continue handler for duplicatekey set @x2=1; #捕獲sqlexception declare continue handler for sqlexception set @x2=1;
流程控制
mysql支持的流程控制有:IF、CASE、LOOP、LEAVE、ITERATE、REPEAT和WHILE語句。
1.IF
語法如下:
IF search_condition THEN statement_list
[ELSEIF search_condition THEN statement_list]...
[ELSE statement_list]
END IF
舉例:求兩個數的最大值
#DELIMITER $$ CREATE PROCEDURE compare(IN n1 INT,IN n2 INT) BEGIN SET @res=0; IF n1 > n2 THEN SET @res=n1; ELSEIF n1 = n2 THEN SET @res=n1; ELSE SET @res=n2; END IF; END #$$ #DELIMITER ;
調用后查詢結果如下:
2.CASE語句
語法如下:
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list]...
[ELSE statement_list]
END CASE
或者:
CASE
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list]...
[ELSE statement_list]
END CASE
我們將以上例子使用case來實現:
#DELIMITER $$ CREATE PROCEDURE compare2(IN n1 INT,IN n2 INT) BEGIN SET @res=0; CASE WHEN n1>n2 THEN SET @res=n1; WHEN n1=n2 THEN SET @res=n1; ELSE SET @res=n2; END CASE; END #$$ #DELIMITER ;
測試:
3.LOOP和LEAVE語句
LOOP可以實現簡單的循環,通常和LEAVE一起使用,LOOP語法如下:
[begin_label:]LOOP
statement_list
END LOOP[end_label]
我們還是以users表為例,當前users表按照 id desc 數據如下:
使用循環向里面插入100行數據:
#DELIMITER $$ CREATE PROCEDURE userinset() BEGIN SET @x=0; ins: LOOP #標簽為ins SET @x=@x+1; IF @x=100 THEN LEAVE ins; #當@x=100的時候,則退出循環 END IF; INSERT INTO users(name,gender) values('周伯通', '男'); END LOOP ins; END #$$ #DELIMITER ;
測試:
call userinset(); select count(1) from users;
4.ITERATE語句
必須在循環中使用,作用是跳過當前循環的剩下的語句,直接進入下一輪循環,相當於一些高級語言中的continue。
只向表中插入奇數行:(仍以users為例):
#delimiter $$ CREATE PROCEDURE inserinfo() BEGIN set @x=1000103; ins: LOOP SET @x=@x+1; IF @x=1000113 THEN LEAVE ins; ELSEIF mod(@x,2)=0 THEN ITERATE ins; END IF; INSERT INTO users(id,name) VALUES(@x,'喬峰'); END LOOP ins; END #$$ #delimiter ;
測試:call inserinfo();
5.REPEAT語句
有條件的循環控制語句,當滿足條件的時候退出循環,語法如下:
[begin_label:]REPEAT
statement_list
UNTIL search_condition
END REPEAT [end_label]
舉例:再在上面例子中插入10行:
#delimiter $$ CREATE PROCEDURE inserinfo2() BEGIN DECLARE x INT DEFAULT 9; ins: REPEAT SET x=x+1; INSERT INTO users(name,gender) VALUES(x,'保密'); UNTIL x>18 END REPEAT; END #$$ #delimiter ;
調用:call inserinfo2();查看數據如下
[begin_lable:]WHILE search_condition DO
statement_list
END WHILE [end_label]
以上的例子如果用while來實現如下:
#delimiter $$ CREATE PROCEDURE inserinfo3() BEGIN DECLARE x INT DEFAULT 9; ins: WHILE x<=18 DO SET x=x+1; INSERT INTO users(name,gender) VALUES(x,'女'); END WHILE; END #$$ #delimiter ;
測試:call inserinfo3();
光標的使用
在存儲過程和函數中,可以使用光標對結果進行循環的處理,語法如下:
聲明光標:
DECLARE cursor_name CURSOR FOR select_statement
OPEN光標:
OPEN cursor_name
FETCH光標:
FETCH cursor_name INTO var_name[,var_name]...
CLOSE光標:
CLOSE cursor_name
舉例:
還是以users表為例,好吧。夜深人靜。。不早了,未完待續....