1. 程式人生 > 其它 >MySQL變數、流程控制和遊標

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檔案中,用其中配置檔案來覆蓋預設的配置檔案