MySQL變數、流程控制和遊標
變數、流程控制和遊標
變數
在MySQL資料庫的儲存過程和函式中,可以使用變數來儲存查詢或計算的中間結果資料,或者輸出最終的結果的資料
系統變數
變數由系統定義,屬於伺服器層面
系統變數的分類
每一個MySQL客戶機成功連線伺服器後,都會產生與之對應的會話(建立一次連線相當於一次會話)。MySQL服務例項會在伺服器記憶體中生成與該會話對應的系統變數,他們的初值都是全域性系統變數值的複製
- 全域性變數(global)
修改針對所有的會話有效,但不能跨重啟(重啟後修改值全面恢復預設值)
- 會話變數(session)
修改針對當前的會話有效,不會影響其他同一會話系統變數的值
注
如果不寫關鍵字,則預設會話級別
有些系統變數只是全域性,有些只是會話,有些既是全域性又是會話
檢視系統變數
- 檢視所有或部分系統變數
#所有
#檢視所有全域性變數
SHOW GLOBAL VARIABLES;
#檢視所有會話變數
SHOW SESSION VARIABLES;
或是
SHOW VARIABLES;
查詢效果:
#部分
#檢視部分會話變數
SHOW GLOBAL VARIABLES LIKE '模糊查詢';
#檢視部分會話變數
SHOW SESSION VARIABLES LIKE '模糊查詢';
- 檢視指定系統變數
MySQL中是以兩個@開始;@@global是全域性,@@session是會話 \ 既全域性又會話。@@符首先標記會話變數,要是沒有會話變數,則標記系統變數
#檢視指定的系統變數
SELECT @@global.變數名;
#檢視指定會話變數
SELECT @@session.變數名;
或是
SELECT @@變數名;
修改系統變數
有時要修改系統變數的值,以便修改當前會話或是MySQL服務例項的屬性、特徵
具體方法:
- 方法一:修改配置檔案,修改後要重啟服務(伺服器如果已經執行,則毫無意義)
- 方法二:在執行期間,使用 SET 指令
#為某個系統變數賦值: #全域性系統變數:針對當前資料庫例項是有效的,一旦重啟伺服器,就失效了 #方式一: SET @@global.變數名 = 要賦的值; #方式二: SET GLOBAL 變數名 = 要賦的值; #會話系統變數:針對當前會話是有效的,一旦建立起新的會話,就失效了 #方式一: SET @@session.變數名 = 要賦的值; #方式二: SET SESSION 變數名 = 要賦的值;
使用者變數
使用者變數的分類
使用者變數是使用者自定義。在MySQL以一個'@'開頭(主要修飾會話使用者變數)
-
會話使用者變數:作用域和會話變數一樣,只對當前來連線會話有效
-
區域性變數:只在BEGIN和END中有效,只在儲存過程和函式中使用
會話使用者變數
- 變數的定義與賦值
#方式一::=或=
SET @變數名 := 值;
SET @變數名 = 值;
#方式二::=或INTO
SELECT @變數名 := 表示式[FROM 等句];
SELECT 表示式 INTO @變數名 [FROM 等句];
- 使用
SELECT @變數名;
區域性變數
定義:使用 DECLARE 去定義一個區域性變數
作用域:僅在它的 BEGIN...END 中有效
位置:只能放在 BEGIN...END 中,且在第一句
- 變數定義
DECLARE 變數名 型別 [default 值]#如果沒有預設值,則初始值null
- 賦值
SET 變數名 = 值;
SET 變數名 := 值;
SELECT 變數名 := 表示式[FROM 等句];
SELECT 表示式 INTO 變數名 [FROM 等句];
- 使用
SELECT 區域性變數名;
舉個例子:
delimiter $
create procedure test_var()
begin
declare a int default 0;
declare b int default 0;
#declare a,b int default 0;
declare emp_name varchar(15);
set a = 1,b := 2;
select name into emp_name from emp1 where id = 3;
select a,b,emp_name;
end $
delimiter ;
call test_var();
對比會話使用者與區域性變數
定義條件與處理程式(異常處理)
定義條件:事先定義程式執行過程中可能遇到的問題
處理程式:定義了在遇到問題時應當採取的處理方式,並且保證儲存過程或函式的繼續執行
注:定義條件和處理程式在儲存函式中都是支援的
定義條件
定義條件就是給錯誤程式碼命名,將一個錯誤名字和一個指定錯誤條件關聯起來。這個名字可以隨後被用在定義處理程式的DECLARE HANDLER語句中
語法格式:
DECLARE 錯誤名字 CONDITION FOR 錯誤碼或錯誤條件(數值型別的錯誤碼);
或是
DECLARE 錯誤名字 CONDITION FOR sqlstate '(字串型別的錯誤程式碼)';
錯誤碼說明:
- MySQL_error_code和sqlstate_value都可以表示MySQL的錯誤
MySQL_error_code:數值型別錯誤程式碼
sqlstate_value:長度為s的字串型別的錯誤程式碼
舉個例子
DECLARE Field_Not_Be_NULL CONDITION FOR 1048;
#錯誤名字 錯誤碼
定義處理程式
可以為MySQL執行過程中發生的某種型別的錯誤定義特殊的處理程式。(在一開頭就編寫)
語法格式:
DECLARE 處理方式 HANDLER FOR 錯誤型別 處理語句;
- 處理方式:
CONTINUE:遇到錯誤不處理,繼續執行
EXIT:遇到錯誤馬上退出
UNDD:遇到錯誤後撤回之前的操作,MySQL暫時不支援這樣的操作
- 錯誤型別(條件):
SQLSTATE'字串錯誤碼':長度是s的字串型別錯誤碼
MySQL_error_code:數值型別錯誤碼
錯誤名稱:定義條件起的名字
SQLWARNING:匹配01開頭的SQLSTATE錯誤碼
NOT FOUND:匹配02開頭的SQLSTATE錯誤碼
SQLEXCEPTION:匹配既不是SQLWARNING,也不是NOT FOUND的SQLSTATE型別錯誤碼
- 處理語句
簡單句:SET 變數 = 值;
複雜句:BEGING ... END;
舉個例子
DECLARE CONTINUE HANDLER FOR 1048 SET @error = -1;
#處理方式:繼續 錯誤碼 讓error = 1
流程控制
類比其他語言的流程控制(我這裡會寫的簡單一點),在執行語句時記得加上WHERE來限制
控制儲存過程中SQL語句的執行順序。
只要是程式,流程就分為三大類
- 順序結構:從上到下執行
- 分支結構:按給出的條件執行,二選一或是多選一
- 迴圈結構:在一定條件下,執行一組語句
針對於MySQL,的流程控制主要有三類(只用於儲存程式)
- 條件判斷語句:IF語句和CASE語句
- 迴圈語句:LOOP、WHILE、REPEAT語句
- 跳轉語句:ITERATE、LEAVE語句
分支結構1(IF)
語句中可以沒有else
語法格式;
IF 表示式1 THEN 操作1
ELSEIF 表示式2 THEN 操作2
...
ELSE 操作N
END IF;
分支結構2(CASE)
語法格式:
#情況一:
CASE 表示式
WHEN 值1 THEN 操作1;
WHEN 值2 THEN 操作2;
...
ELSE 操作N;
END[case];#BEGIN END 中要加case
#情況二:
CASE
WHEN 條件1 THEN 操作1;
WHEN 條件2 THEN 操作2;
...
ELSE 操作N;
END[case];#BEGIN END 中要加case
迴圈結構1(LOOP)
迴圈語句有四個條件:
- 初始條件
- 迴圈條件
- 迴圈體
- 迭代條件
LOOP內語句一直重複執行,知道迴圈退出(使用LEAVE子句)
語法格式:
[loop_label:] LOOP
迴圈體;
END LOOP [loop_label];
舉個例子:
#從1一每次加1直到為10輸出
BEGIN
#初始化
SET a int default 1;
loop_label:LOOP
#迴圈主體(此時省略了,程式太過於簡單)
#迭代條件
SET a = a + 1;
#迴圈條件
IF a >= 10 THEN LEAVE loop_label;
END IF;
END LOOP loop_label;
END $
迴圈結構2(WHILE)
while不控制迴圈:while true
語法格式:
[while_label:]WHILE 迴圈條件 DO
迴圈體;
END WHILE [while_label];
迴圈結構3(REPEAT)
類似於DO WHILE,至少執行一次
語法格式:
[repeat_label:]REPEAT
迴圈體;
UNTILL 結束迴圈語句 #沒有;
END REPEAT [repeat_label];
跳轉語句1(LEAVE)
類似於break,用在迴圈語句內或是 在BEGIN ...END 中使用,可以跳出迴圈體或是程式
要跳出誰,給誰加標籤
語法格式:
LEAVE 標籤名;
舉個例子:
#部分程式碼不完整
SET a = a + 1;
IF a >= 10 THEN LEAVE loop_label;
#標籤名
END IF;
跳轉語句2(ITERATE)
類似於 continue ,只能在迴圈語句中使用,跳過本次迴圈,進入下一次迴圈
語法格式:
ITERATE 標籤名;
遊標
什麼是遊標(游標)
可以定位指定的記錄並可以對其操作(充當指標)
使用遊標的步驟
遊標必須在宣告處理程式之前宣告,並且變數和條件也必須在宣告遊標或是處理程式之前宣告
- 第一步:宣告遊標
MySQL中,使用DECLARE關鍵字來宣告遊標,語法格式如下:
DECLARE 遊標名 CURSOR FOR 查詢語句(結果集);
如果是Oracle或是PostgreSQL中,語法格式如下:
DECLARE 遊標名 CURSOR IS 查詢語句(結果集);
- 第二步:開啟遊標
OPEN 遊標名;
- 第三步:使用遊標
FETCH 遊標名 INTO 查詢結果的欄位1,欄位2,...(要一一對應,名字之間要有關聯,這樣易呼叫);
#讓遊標讀取當前行,遊標指標指向下一行
- 第四步:關閉遊標
CLOSE 遊標名;
如果不及時關閉,遊標會佔用系統資源,影響系統執行效率
舉個例子:
#給出一個工資總和的上限數,讓所有員工的工資進行降序排列,並依次相加,直到相加的工資總數大於所給的工資總上限數,計算相加人數
#部分程式碼(儲存過程未寫)
上限數:limit_total_salry double(in)
相加人數:total_count int(out)
#宣告區域性變數(儲存每個人的工資,儲存相加的數,儲存相加人數):
DECLARE emp_sal double;
DECLARE sum_sal int default 0;
DECLARE emp_count int default 0;
#宣告遊標
DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees ORDER BY salary DESC;
#開啟遊標
OPEN emp_cursor;
#使用遊標
WHILE sum <= limit_total_salry DO
FETCH emp_cursor INTO emp_sal;
SET sum_sal = emp_sal + sum_sal;
emp_count = emp_count + 1;
END WHILE;
#賦值相加人數
SET total_count = emp_count;
#關閉遊標
CLOSE emp_cursor;
遊標小結
- 優點
遊標為逐條讀取資料提供瞭解決方案。
可以在儲存程式中使用,效率高,程式也會更簡潔
- 不足
會帶來一些效能的問題,使用遊標會對資料加鎖,在業務併發量大的時候,會損耗系統資源(所以要養成關閉的習慣)
補充:MySQL8.0的新特性—全域性變數的持久化
重啟伺服器後,修改的全域性變數依舊有效
MySQL8.0新增了 SET PERSIST 命令,格式如下:
SET PERSIST global 全域性變數名字 = 1000;
MySQL會將給命令的配置儲存到資料目錄下的mysql-auto.cnf檔案中,用其中配置檔案來覆蓋預設的配置檔案