1. 程式人生 > >學會使用MySQL中自定義函式和儲存過程

學會使用MySQL中自定義函式和儲存過程

一、快速瞭解什麼是儲存過程和函式?

  儲存過程和函式是事先經過編譯並存儲在資料庫中的一段 SQL 語句的集合,呼叫儲存過程
和函式可以簡化應用開發人員的很多工作,減少資料在資料庫和應用伺服器之間的傳輸,對
於提高資料處理的效率是有好處的。

  在對儲存過程或函式進行操作時,需要首先確認使用者是否具有相應的許可權。例如,建立儲存過程或者函式需要 CREATE ROUTINE 許可權,修改或者刪除儲存過程或者函式需要 ALTER
ROUTINE 許可權,執行儲存過程或者函式需要 EXECUTE 許可權。

   儲存過程和函式的區別在於函式必須有返回值,而儲存過程沒有,儲存過程的引數可以使用IN、OUT、INOUT 型別,而函式的引數只能是 IN 型別的。

二、語法(MySQL8.0官方語法)

  在對儲存過程或函式進行操作時,需要首先確認使用者是否具有相應的許可權。例如,建立儲存過程或者函式需要 CREATE ROUTINE 許可權,修改或者刪除儲存過程或者函式需要 ALTER
ROUTINE 許可權,執行儲存過程或者函式需要 EXECUTE 許可權。

1. 建立、修改儲存過程或者函式

-- 建立
CREATE
[DEFINER = { user | CURRENT_USER }]
PROCEDURE sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body

CREATE
    [DEFINER = { user | CURRENT_USER }]
    FUNCTION sp_name ([func_parameter[,...]])
    RETURNS type
    [characteristic ...] routine_body

proc_parameter:
    [ IN | OUT | INOUT ] param_name type

func_parameter:
    param_name type

type:
    Any valid MySQL data type

characteristic:
    COMMENT 'string'
  | LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }

routine_body:
    Valid SQL routine statement


-- 修改
ALTER {PROCEDURE|FUNCTION] proc_name [characteristic ...]

characteristic:
    COMMENT 'string'
  | LANGUAGE SQL
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }

2.刪除儲存過程或者函式

DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name

3.檢視儲存過程或者函式

-- 檢視狀態
SHOW {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern']
-- 檢視定義
SHOW CREATE {PROCEDURE | FUNCTION} sp_name
-- 通過檢視 information_schema. Routines 瞭解儲存過程和函式的資訊
select * from routines where ROUTINE_NAME = sp_name \G

4.呼叫儲存過程或者函式

--呼叫儲存過程
CALL sp_name([parameter[,...]])
--呼叫函式
SELECT sp_name([parameter[,...]])

三、變數的使用、流程控制語句和游標的使用

  在使用儲存過程或者函式之前我們要學會使用變數和流程控語句的使用,因為這是業務邏輯中重要的組成部分。我們可以通過 DECLARE 可以定義一個區域性變數,變數的定義必須寫在複合語句的開頭,並且在任何其他語句的前面。可以一次宣告多個相同型別的變數。如果需要,可以使用 DEFAULT 賦預設值。也可以通過set為變數賦值。可以使用 IF、CASE、LOOP、LEAVE、ITERATE、REPEAT 及 WHILE 語句進行流程的控制

變數的分類

  1. 區域性變數: 使用在儲存過程或函式中,這樣的變數使用 declare 進行宣告,使用 set 進行賦值。作用域是過程中。

     --語法
     DECLARE var_name[,...] type [DEFAULT value]
     SET var_name = expr [, var_name = expr] ...
     
     --案例
     -- 定義變數並賦予初始值
     declare num int default 0;
     -- 給變數賦值
     set num=100;
    
  2. 使用者變數: 使用者根據自己的需求進行定義的變數,只存在當前回話中,需要用 @ 符號進行修飾。

     -- 建立
     set @num = 666;
     select @num :=666;
    
  3. 系統變數: 系統自帶的,內建的變數

    可以通過命令列的方式進行設定,如:mysqld –default_storage_engine=MyISAM

    可以在 my.ini 配置檔案中進行配置,如:default_storage_engine=MyISAM

    可以通過 set 的方式進行動態設定

    系統變數,分為兩類:

     -- 全域性的(global)
     show global variables;
     set global default_storage_engine=MyISAM;
     set @@global.default_storage_engine=MyISAM;
    
     -- 會話的(session)
     -- 檢視全域性變數的方法
     show session variables;
     show local variables;
     show variables;
     -- 設定全域性變數的寫法
     set session default_storage_engine=MyISAM;
     set local   default_storage_engine=MyISAM;
     set @@session.default_storage_engine=MyISAM;
     set @@local.default_storage_engine=MyISAM;
     set default_storage_engine=MyISAM;
     set @@default_storage_engine=MyISAM;
    

流程控制語句

1.IF 語句

IF search_condition THEN statement_list
	[ELSEIF search_condition THEN statement_list] ...
	[ELSE statement_list]
END IF

2.case 語句

CASE case_value
	WHEN when_value THEN statement_list
	[WHEN when_value THEN statement_list] ...
	[ELSE statement_list]
END CASE

or:

CASE
	WHEN search_condition THEN statement_list
	[WHEN search_condition THEN statement_list] ...
	[ELSE statement_list]
END CASE

3.loop迴圈(leave退出迴圈,iterate跳過迴圈,二者適用所有的迴圈)

[label:] LOOP
	statement_list
	-- 如果滿足條件退出迴圈
	-- 如果沒有loop沒有配合leave使用,將會形成死迴圈
	IF search_condition THEN statement_list
		leave [label];--跳出迴圈
		-- 跳過迴圈
		-- iterate [label];
	END IF;
END LOOP [label]

4.repeat語句

REPEAT
	statement_list
UNTIL search_condition END REPEAT

5.while語句

WHILE search_condition DO
	statement_list
END WHILE

游標的使用

在儲存過程和函式中可以使用游標對結果集進行迴圈的處理。游標的使用包括游標的宣告、
OPEN、FETCH 和 CLOSE,其語法分別如下。

-- 宣告游標:
DECLARE cursor_name CURSOR FOR select_statement
-- OPEN 游標:
OPEN cursor_name
-- FETCH 游標:
FETCH cursor_name INTO var_name [, var_name] ...
-- CLOSE 游標:
CLOSE cursor_name

四、案例集合

注意:在命令列視窗建立函式或者儲存過程之前,要改變一下結束符";",使用delimiter關鍵字進行改變,建立完畢後再改變過來。

1. 沒有引數,只含有返回值的自定義函式

-- 更改結束符
delimiter //
-- 如果函式存在就刪除
drop function if exits fun_randid;
-- 建立函式
create function fun_randid() returns varchar(100)
begin
	return UUID();
end
//
--函式建立結束
-- 更改結束符
delimter ;

-- 函式的呼叫
select fun_randid();

2. in型別引數的自定義函式

delimiter //
drop function if exits fun_randid;
create function fun_randid(size int) returns varchar(100)
begin
	return substring(UUID(),0,size);
end//
delimter ;

-- 呼叫
select fun_randid(10);

3.沒有引數的儲存過程

delimiter //
drop procedure if exits proc_select_data;
create procedure proc_select_data()) 
begin
	select * from book_info limit 10;
end//
delimter ;

-- 呼叫
call proc_select_data

4.in型別引數的的儲存過程

delimiter //
drop procedure if exits proc_insert_data;
create procedure proc_insert_data(in name varchar(25)) -- in可以省略
begin
	insert into book_info(book_name) values(name);
end//
delimter ;

-- 呼叫
call proce_insertdata('MySQL從入門到精通');

5.out型別引數的儲存過程

delimiter //
drop procedure if exits proc_count_book;
create procedure proc_count_book(out count int)
begin
	select count(*) into count from book_info;
end//
delimter ;

--呼叫
call proce_count_book(@count);
select @count;

6.inout型別引數的儲存過程

delimiter //
drop procedure if exits proc_insert_count;
create procedure proc_insert_count(inout data varchar(25))
begin
	-- 拿到引數插入表中
	insert into book_info(book_name) values(data);
	-- 設定引數的值
	select count(*) into data from book_info;
end//
delimter ;

--呼叫
set @data = 'MySQL效能調優'; 	-- 設定傳入引數的值
call proc_insert_count(@data);	-- 呼叫儲存過程
select @data;					-- 得到輸出引數的值

7.綜合應用——使用游標迴圈結果集的儲存過程

delimiter //
drop procedure if exits proc_concat_string;
create procedure proc_concat_string(out str text)
begin
	declare count int;
	declare str_concat text default '';
	
	-- 注意遊標的定義一定要在所有的自定義的變數後面,否則會報錯,錯誤編號1337
	-- 1.定義遊標
	declare c_name cursor for select rand_string from rand limit 10;
	-- 2.捕獲異常,如果捕捉到了將count設定為1
	declare continue handler for not found set count = 1;
	
	set str = '';-- 設定輸出引數初始值
	-- 3.開啟遊標
	open c_name;
	point:loop
		-- 4.使用遊標
		fetch c_name into str_concat; 	
		set str = concat(str,str_concat);
		-- 5.設定退出迴圈
		if count = 1 then  
			leave point;
		end if;
	end loop;
	-- 6.關閉遊標
	close c_name; 
end//
delimiter ;
-- 呼叫
set @str='';
call proc_concat_string(@str);
select @str;

8.綜合應用——自定義函式生成隨機字串

delimiter //
drop function if exits fun_randString;
-- 兩個引數:生成的隨機字串的長度和字串型別(1:字元;2:數字;其他:字元數字混合)
create function fun_randString(size int,type int) returns varchar(255)
BEGIN
	declare chars varchar(26) default 'qwertyuiopasdfghjklzxcvbnm';
	declare num varchar(10) default '1234567890';

	declare string varchar(255) default '';
	declare rand_string varchar(255) default '';
	declare count int default 0;

	set string = case type 
		when 1 then chars
		when 2 then num
		else concat(chars,num)
	end;
	
	while count<size do
		set rand_string = concat(rand_string,substring(string,floor(1+rand()*length(string)), 1));
		set count = count + 1;
	end while;

-- 	REPEAT
-- 		set rand_string = concat(rand_string,substring(string,floor(1+rand()*length(string)),1));
-- 		set count = count + 1;
-- 	until count>=size end repeat;

	return rand_string;
END//
delimiter ;

-- 呼叫
select fun_randString(10, 2);