文章目錄
- 一、存儲過程
- 1.1、語法:
- 1.2、示例
- 1.2.1、無參數的存儲過程
- 1.2.3、只有一個 in 參數的存儲過程
- 1.2.3、包含 in 參數和 out 參數的存儲過程
- 1.2.4、包含 inout 參數的存儲過程
- 1.2.5、附:根據 時間 修改 狀態:
- 1.3、區別:
- 二、自定義函數 udf(user-defined function )
- 三、複合結構
- 四、變量
- 五、事務和回滾點
- 六、流程控制
- 1、if 函數
- 2、if 條件判斷
- 3、case when 條件判斷
- 4、loop 循環
- 5、leave 跳出循環(break)
- 6、iterate 跳出本次循環(continue)
- 7、repeat (先)循環
- 8、while (先判斷,再)循環
- 七、 循環示例
一、存儲過程
1.1、語法:
1)創建存儲過程
drop procedure if exists [存儲過程名] ;
delimiter [結束標記]
create procedure [存儲過程名]([參數1], [參數2] ...)
begin
[存儲過程體(一組合法的sql語句)]
end [結束標記]
delimiter ;
參數,其格式爲 [in|out|inout] parameter_name type
- in 表示 輸入參數;
- out 表示 輸出參數;
- inout 表示 此參數既可以輸入也可以輸出;
- param_name 表示 參數名稱;
- type 表示 參數的類型。
delimiter [結束標記]
本身與存儲過程的語法無關,用於表示存儲過程的結束。
最後一個命令 delimiter ;
將 分隔符 改回 分號。
存儲過程體中可以有多條sql語句,如果僅僅一條sql語句,則可以省略 begin end` 。
2)調用存儲過程
call [存儲過程名]([ proc_parameter [,proc_parameter ...]])
call [存儲過程名]
說明:
當無參數時,可以省略括號,不寫;
當有參數時,不可省略括號。
3)存儲過程修改 : 修改存儲過程,就是刪除重建。
4)刪除存儲過程: drop procedure [if exists] sp_name
1.2、示例
drop table if exists `t_user`;
create table `t_user` (
`id` int not null auto_increment,
`name` varchar(20) not null,
primary key (`id`)
) engine=innodb auto_increment=1 default charset=utf8;
1.2.1、無參數的存儲過程
drop procedure if exists myproc1;
delimiter $
create procedure myproc1()
begin
insert into t_user values(null, 'Jas'),(null, 'Joy');
end $
delimiter ;
調用,並查看結果:
1.2.3、只有一個 in 參數的存儲過程
drop procedure if exists myproc2;
delimiter $
create procedure myproc2(in userId int)
begin
select name from t_user where id = userId;
end $
delimiter ;
調用,並查看結果:
1.2.3、包含 in 參數和 out 參數的存儲過程
drop procedure if exists myproc3;
delimiter $
create procedure myproc3(in userId int, out username varchar(20))
begin
select name into username # 將查詢到的用戶名賦值給 username
from t_user where id = userId;
end $
delimiter ;
調用,並查看結果:
- 創建存儲過程 myproc3,包含一個in參數和一個out參數 ;
- 調用時,傳入刪除的id 和 全局變量 @username ;
- select @username 輸出結果。
1.2.4、包含 inout 參數的存儲過程
drop procedure if exists myproc4;
delimiter $
create procedure myproc4(inout a int)
begin
set a = a * 2;
end $
delimiter ;
調用存儲過程,並查詢結果:
1.2.5、附:根據 時間 修改 狀態:
delimiter $$
use `exam9` $$ ## exam9 是數據庫
drop procedure if exists `updateStatus` $$ ## 如果存在,就刪除
create procedure `updateStatus`()
begin
update exam set `status`="已結束" where `status` != "已結束" and (now() - endtime)>0 ;
update exam set `status`="正在答題" where `status` != "已結束" and ( now() - starttime)>=0
and (now() - endtime)<=0;
update haulinfo set bigstatus="已結束" where bigstatus != "已結束" and (curdate() - bigenddate)>0;
update haulinfo set bigstatus="進行中" where (curdate() - bigenddate)<=0 and (curdate() - bigbegindate)>=0;
update exam set bigstatus=(select bigstatus from haulinfo where bigid=exam.bigid);
end $$
delimiter ;
1.3、區別:
1.3.1、存儲過程的優點:
- 存儲過程 就是把經常使用的 sql語句 或 業務邏輯封裝起來,預編譯保存在數據庫中,當需要的時候從數據庫中直接調用,省去了編譯的過程;
- 提高了運行速度;
- 同時降低網絡數據傳輸量( 不用傳一堆sql代碼快,而是傳一個存儲過程名字和幾個參數)。
1.3.2、存儲過程 與 函數 的區別
-
一般來說,存儲過程實現的功能要複雜一點,而函數的實現的功能針對性比較強。
-
存儲過程可以有返回值也可以沒有返回值,而自定義函數必須要返回值,且返回值有且只有一個。
-
存儲過程一般是作爲一個獨立的部分來執行,而函數可以作爲查詢語句的一個部分來調用,因此它可以在查詢語句中位於
from
關鍵字的後面。 SQL 語句中不可用存儲過程,而可以使用函數。
二、自定義函數 udf(user-defined function )
自定義函數 就像是 abs() 、 concat() 內建函數一樣去擴展 mysql 。
所以,udf 是對 mysql 功能的一個擴展。
2.1、自定義函數 udf
1) 創建 udf
drop function if exists [函數名];
delimiter [結束標記]
create function [函數名]([參數1], [參數2] ...) returns [返回值類型]
begin
[方法體]
return [返回值];
end [結束標記]
delimiter ;
參數,其格式爲 param_name type
,如 username varchar(20)
。
2) 刪除 udf: drop function [函數名]
3)調用 udf : select [函數名](param_value, ...)
2.2、示例
t_user
表中的數據:
2.2.1、 無參數的自定義函數
查詢t_user
中的數據行數,並返回。
drop function if exists myfun1;
delimiter $
create function myfun1() returns int
begin
declare sum int default 0; # 定義局部變量 sum,默認值爲 0
select count(*) into sum # 將查詢的結果賦值給 sum
from t_user;
return sum;
end $
delimiter ;
調用結果:
2.2.2、有參數的自定義函數
drop function if exists myfun2;
delimiter $
create function myfun2(userId int) returns varchar(20)
begin
set @username=''; # 定義系統會話變量
select name into @username # 將用戶名賦值給 username
from t_user
where id = userId;
return @username;
end $
delimiter ;
查看結果:
三、複合結構
3.1、語法格式:
delimiter //
create function if exist deleteById(uid smallint unsigned) returns varchar(20)
begin
delete from t_order where id = uid;
return (select count (id) from son);
end //
delimiter ;
在函數體中,如果包含多條語句, 我們需要把多條語句放到 begin...end
語句塊中。
begin...end
相當於 java 語言中的 { }
。
public int method(int param){
int result=0;
if(param==1){
......
result=......
}else if{param==2}{
....
result=......
}else{
.......
result=......
}
return result;
}
3.2、delimiter 修改默認的結束符
delimiter //
表示 將默認的結束符由 ;
改爲 //
,以後的sql語句都要以 //
作爲結尾 。
3.3、 returns 聲明返回值類型
returns varchar(20)
聲明 返回值 是 20位長度的字符串 。
returns int
聲明返回值 int 。
3.4、reurn 定義 返回值
reurn 語句
也包含在begin...end
中。
3.5、declare 定義局部變量
declare var_name[,varname]...date_type [default value];
簡單來說就是:
declare 變量1[,變量2,... ]變量類型 [default 默認值]
這些變量的作用範圍是在begin…end程序中,而且定義局部變量語句必須在begin…end的第一行定義
示例:
delimiter //
create function addNum(x smallint unsigned, y smallint unsigned) returns smallint
begin
declare a, b smallint unsigned default 10; ### 定義局部變量
set a = x, b = y;
return a+b;
end //
delimiter ;
上邊的代碼只是把兩個數相加,當然,沒有必要這麼寫,只是說明局部變量的用法,還是要說明下:這些局部變量的作用範圍是在 begin...end
程序中
四、變量
4.1、系統變量
1)全局變量 (global)
作用域:針對於所有會話(連接)有效,但不能跨重啓。重啓後,配置失效。
# 查看所有全局變量
show global variables;
# 查看滿足條件的部分系統變量
show global variables like '%char%';
# 查看指定的系統變量的值
select @@global.autocommit;
# 爲某個系統變量賦值
set @@global.autocommit=0;
set global autocommit=0;
2)會話變量(默認 session )
作用域:針對於當前會話(連接)有效
系統變量,如果不加 global
和 session
,則默認就是 session 。
# 查看所有會話變量
show session variables; ## 等價於 show variables;
# 查看滿足條件的部分會話變量
show session variables like '%char%';
# 查看指定的會話變量的值
select @@autocommit;
select @@session.tx_isolation;
# 爲某個會話變量賦值
set @@session.tx_isolation='read-uncommitted';
set session tx_isolation='read-committed';
4.2、自定義變量
1)用戶變量 (全局的變量)
作用域: 用戶變量 在當前連接(即當前會話)中都有效。
用戶變量的特點:
- 不需要聲明,直接使用,
- 前後數據類型可以不一樣。
示例:
同一個用戶變量,前後可以接收不同類型的賦值。
age 是 int
name 是varchar(20)
set @tmpVal = age; ## 將age的值賦給 @tmpVal
set @tmpVal = name; ## 將name的值賦給 @tmpVal
聲明並初始化:
set @變量名=值;
set @變量名:=值;
select @變量名:=值;
賦值:
## 方式一:一般用於賦簡單的值
set 變量名=值;
set 變量名:=值;
select 變量名:=值;
## 方式二:一般用於賦表 中的字段值
select 字段名或表達式 into 變量
from 表;
使用:
select @變量名;
2)局部變量
作用域: 局變變量在 begin end 語句中有效,超過範圍即失效。
聲明:
declare 變量名 類型 【default 值】;
declare name varchar(20) ;
declare num int default 0;
局部變量 必須在 begin end 的第一行 。
賦值:
# 方式一:一般用於賦簡單的值
set 變量名=值;
set 變量名:=值;
select 變量名:=值;
# 方式二:一般用於賦表 中的字段值
select 字段名或表達式 into 變量
from 表;
使用:
select 變量名
3)用戶變量與 局部變量的區別:
- | 作用域 | 定義位置 | 語法 |
---|---|---|---|
用戶變量 | 當前會話 | 會話的任何地方 | 加@符號,不用指定類型 |
局部變量 | 定義它的 begin end 中 | begin end 的第一句話 | 一般不用加@ ,需要指定類型 |
五、事務和回滾點
5.1、事務
set autocommit=0; ## 1、取消自動提交
start transaction; ## 2、開啓事務
要執行的操作
commit; ## 3、提交事務
rollback; ## 4、回滾事務
5.2、存儲過程和函數使用事務的格式:
begin
set autocommit=0; ## 取消自動提交
start transaction; ## 開啓事務
要執行的操作
commit; ## 提交事務
end ;
5.3、保存點
在事務中,設置保存點,
當回滾時,能回滾到這個保存點,
但是 保存點 之前的執行不會回滾 。
set autocommit=0; ## 取消自動提交
start transaction;
..... ## SQL語句
savepoint aa; ## 設置保存點,aa 是自定義的名稱,保持唯一
..... ## SQL語句
rollback to aa ## 回滾到保存點。 但是保存點前面執行的SQL執行依然有效。
5.4、示例: 快速生成 1000W 測試數據
MySQL 高級–優化 —— 複合索引(多列索引、聯合索引)的定義、區別、創建和理解 中快速生成 1000W 測試數據。
六、流程控制
存儲過程 和 函數 中可以使用 流程控制語句 來 控制SQL 的執行。
mysql中可以使用 if 、case 、loop、leave、iterate 、repeat 和 while 語句 來進行流程控制。
每個流程中可能包含一個單獨語句,或者是使用 begin...end
構造的複合語句,構造可以被嵌套。
1、if 函數
語法:
if(條件,值1,值2)
如果條件成立,則返回 值1,否則返回 值2
特點:可以用在任何位置。
2、if 條件判斷
根據是否滿足條件,將執行不同的語句。
特點:
與 if 函數 的不同,這個if 條件只能用在 begin end
中 !!
1) if 語法:
if 表達式1 then 語句1;
elseif 表達式2 then 語句2;
...
else 語句n;
end if;
參數說明:
特點:
只能用在 begin end 中 !!
注意: mysql還有一個if()函數,不同於這裏描述的 if 語句。
2) if 示例:
if age>20 then
set @count1=@count1+1;
elseif age=20 then
set @count2=@count2+1;
else
set @count3=@count3+1;
end if;
說明:
根據age與20的大小關係來執行不同的set語句。
如果age值大於20,那麼將count 1的值加1;
如果age值等於20,那麼將count 2的值加1;
其他情況將 count3 的值加 1 。
最後,if 語句都需要使用 end if
來結束。
3、case when 條件判斷
case when 也用來進行條件判斷,其可以實現 比 if 更復雜的條件判斷。
1)case when 語法:
類似於 switch
case 表達式
when 值1 then 結果1或語句1 ## 如果是語句,需要加分號
when 值2 then 結果2或語句2 ## 如果是語句,需要加分號
...
else 結果n或語句n ## 如果是語句,需要加分號
end 【case】 ## 如果是放在begin end中,則結尾處需要加上case,如果放在select後面不需要
說明:
如果是在 begin end
中,則需要在每個 語句後面加 分號,case 結尾處加 case
;
如果放在 select 後面,則每個語句後面不需要加分號, case 結尾處不需要加 case
。
2)case when 語法2:
case
when 表達式1 then 結果1或語句1 ## 如果是語句,需要加分號
when 表達式2 then 結果2或語句2 ## 如果是語句,需要加分號
...
else 結果n或語句n ## 如果是語句,需要加分號
end 【case】 ## 如果是放在begin end中,則結尾處需要加上case,如果放在select後面不需要
說明:
語法1 的when 後面是值,
語法2 的when 後面是 表達式,可以進行區間的判斷,
這個兩個語法最大的區別。
示列:
4、loop 循環
loop 可以使某些特定的語句重複執行,實現一個簡單的循環。
但是 loop 本身沒有停止循環的語句,必須使用 leave、iterate 等才能停止循環。
1) loop 語法:
[begin_label:] loop
statement_list
end loop [end_label]
參數說明:
-
begin_label 、end_label 分別表示 循環開始 和 結束的標誌,這兩個標誌必須相同,而且都可以省略;
-
statement_list 表示需要循環執行的語句。
2) loop 示例
add_num: loop
set @count=@count+1;
end loop add_num ;
說明:
循環執行 count 加1的操作。
因爲沒有跳出循環的語句,這個循環成了一個死循環。
loop 循環都以 end loop
結束。
5、leave 跳出循環(break)
leave 用於跳出循環。
leave 用於 loop、repeat、while ,中斷 並跳出循環。
1) leave 語法:
leave label
參數說明:
label 表示 循環的標誌。
2)leave 示例:
add_num: loop
set @count=@count+1;
if @count=100 then
leave add_num ;
end loop add_num ;
循環執行 count 加1的操作。
當 count 的值等於100時,則leave語句跳出循環。
6、iterate 跳出本次循環(continue)
iterate 也是跳出循環。但是,iterate 語句是跳出本次循環,然後直接進入下一次循環。
iterate 用於 loop、repeat、while 語句跳過本次循環。
1) iterate 語法:
iterate label
參數說明:
- label 表示循環的標誌。
2)iterate 示例:
add_num: loop
set @count=@count+1;
if @count=100 then
leave add_num ;
else if mod(@count,3)=0 then
iterate add_num;
select from employee ;
end loop add_num ;
說明:
循環執行 count 加1的操作。
當 count 值爲100時結束循環。
如果 count 的值能夠整除3,則跳出本次循環,不再執行下面的select語句。
leave 和 iterate 的區別
相同點:
leave 和 iterate 都用來跳出循環語句,但兩者的功能是不一樣的。
不同點:
leave 是跳出整個循環,然後執行循環後面的程序。
iterate 是跳出本次循環,然後進入下一次循環。
7、repeat (先)循環
repeat 是有條件控制的循環。當滿足特定條件時,就會跳出循環語句。
1)repeat 語法:
repeat
statement_list
until search_condition # until 後面沒有分號(;),否則 報錯
end repeat;
參數說明:
-
statement_list 表示 循環的執行語句;
-
search_condition 表示 結束循環的條件,滿足該條件時循環結束。
2) repeat 示例:
repeat
set @count=@count+1;
until @count=100
end repeat ;
循環執行count 加1的操作。
當 count 值爲10 0時 結束循環。
repeat循環都用end repeat結束。
8、while (先判斷,再)循環
while 也是有條件控制的循環語句。但while 和 repeat 是不一樣的。
while 是當滿足條件時,執行循環內的語句。
1) while 語法:
while search_condition do
statement_list
end while ;
參數說明:
-
search_condition 表示 循環執行的條件,滿足該條件時循環執行;
-
statement_list 表示 循環的執行語句。
2) while 示例:
while @count<100 do
set @count=@count+1;
end while ;
循環執行count 加1的操作。
如果 count 值小於100時執行循環;
如果 count 值等於100了,則跳出循環。
while 循環需要使用end while 來結束。
七、 循環示例
1、 loop 循環的示例
delimiter $$
drop procedure if exists `sp_testloop` $$
create procedure `sp_testloop`(
in p_number int, #要循環的次數
in p_startid int #循環的起始值
)
begin
declare v_val int default 0;
set v_val=p_startid;
loop_label: loop #循環開始
set v_val=v_val+1;
if(v_val>p_number)then
leave loop_label; # 終止循環
end if;
end loop;
select concat('testloop_',v_val) as tname;
end $$
delimiter ;
call sp_testloop(1000,0);
2、while循環的示例
delimiter $$
drop procedure if exists `sp_test_while`$$
create procedure `sp_test_while`(
in p_number int, #要循環的次數
in p_startid int #循環的起始值
)
begin
declare v_val int default 0;
set v_val=p_startid;
outer_label: begin #設置一個標記
while v_val<=p_number do
set v_val=v_val+1;
if(v_val=100)then
leave outer_label; #滿足條件,終止循環,跳轉到 end outer_label標記
end if;
end while;
select '我是while外,outer_label內的SQL';## 這句SQL在outer_label代碼塊內,所以level後,這句SQL將不會執行;
#只要是在outer_label代碼塊內 任意位置 Leave outer_label,那麼Leave後的代碼將不再執行
end outer_label;
select concat('test',v_val) as tname;
end$$
delimiter ;
call sp_test_while(1000,0);
3、repeat 循環的示例
delimiter $$
drop procedure if exists `sp_test_repeat`$$
create procedure `sp_test_repeat`(
in p_number int, #要循環的次數
in p_startid int #循環的起始值
)
begin
declare v_val int default 0;
set v_val=p_startid;
repeat #repeat循環開始
set v_val=v_val+1;
until v_val > p_number #終止循環的條件,注意這裏不能使用';'分號,否則報錯
end repeat; #循環結束
select concat('test',v_val) as tname;
end$$
delimiter ;
call sp_test_repeat(1000,0);