1. 程式人生 > 其它 >mysql:變數、異常處理(孬)

mysql:變數、異常處理(孬)

變數

系統變數

  • 分類

變數由系統定義,不是使用者定義,屬於伺服器層面。啟動MySQL服務,生成MySQL服務例項期間,MySQL將為MySQL伺服器記憶體中的系統變數賦值,這些系統變數定義了當前MySQL服務例項的屬性、特徵。這些系統變數的值要麼是編譯MySQL時引數的預設值,要麼是配置檔案(例如my.ini等)中的引數值。大家可以通過網址 https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html 檢視MySQL文件的系統變數。

系統變數分為全域性系統變數(需要新增global 關鍵字)以及會話系統變數(需要新增 session

關鍵字),有時也把全域性系統變數簡稱為全域性變數,有時也把會話系統變數稱為local變數。如果不寫,預設會話級別。靜態變數(在 MySQL 服務例項執行期間它們的值不能使用 set 動態修改)屬於特殊的全域性系統變數。

每一個MySQL客戶機成功連線MySQL伺服器後,都會產生與之對應的會話。會話期間,MySQL服務例項會在MySQL伺服器記憶體中生成與該會話對應的會話系統變數,這些會話系統變數的初始值是全域性系統變數值的複製。如下圖:

  • 全域性系統變數針對於所有會話(連線)有效,但不能跨重啟
  • 會話系統變數僅針對於當前會話(連線)有效。會話期間,當前會話對某個會話系統變數值的修改,不會影響其他會話同一個會話系統變數的值。
  • 會話1對某個全域性系統變數值的修改會導致會話2中同一個全域性系統變數值的修改。

在MySQL中有些系統變數只能是全域性的,例如 max_connections 用於限制伺服器的最大連線數;有些系統變數作用域既可以是全域性又可以是會話,例如 character_set_client 用於設定客戶端的字符集;有些系統變數的作用域只能是當前會話,例如 pseudo_thread_id 用於標記當前會話的 MySQL 連線 ID。


  • 查詢
#檢視所有全域性變數
SHOW GLOBAL VARIABLES;

#檢視所有會話變數
SHOW SESSION VARIABLES;
#或
SHOW VARIABLES;
#舉例
SHOW GLOBAL VARIABLES LIKE 'admin_%';

檢視指定系統變數

作為 MySQL 編碼規範,MySQL 中的系統變數以兩個“@”開頭,其中“@@global”僅用於標記全域性系統變數,“@@session”僅用於標記會話系統變數。“@@”首先標記會話系統變數,如果會話系統變數不存在,則標記全域性系統變數。

#檢視指定的系統變數的值
SELECT @@global.變數名;

#檢視指定的會話變數的值
SELECT @@session.變數名;
#或者
SELECT @@變數名;
  • 修改系統變數的值

有些時候,資料庫管理員需要修改系統變數的預設值,以便修改當前會話或者MySQL服務例項的屬性、特徵。具體方法:

方式1:修改MySQL配置檔案,繼而修改MySQL系統變數的值(該方法需要重啟MySQL服務)

方式2:在MySQL服務執行期間,使用“set”命令重新設定系統變數的值

#為某個系統變數賦值
#方式1:
SET @@global.變數名=變數值;
#方式2:
SET GLOBAL 變數名=變數值;


#為某個會話變數賦值
#方式1:
SET @@session.變數名=變數值;
#方式2:
SET SESSION 變數名=變數值;

舉例:

SELECT @@global.autocommit;
SET GLOBAL autocommit=0;
SELECT @@session.tx_isolation;
SET @@session.tx_isolation='read-uncommitted';
SET GLOBAL max_connections = 1000;
SELECT @@global.max_connections;

使用者變數

使用者變數分類

使用者變數是使用者自己定義的,作為 MySQL 編碼規範,MySQL 中的使用者變數以一個“@”開頭。根據作用範圍不同,又分為會話使用者變數區域性變數

  • 會話使用者變數:作用域和會話變數一樣,只對當前連線會話有效。

  • 區域性變數:只在 BEGIN 和 END 語句塊中有效。區域性變數只能在儲存過程和函式中使用。


會話使用者變數

  • 變數的定義
#方式1:“=”或“:=”
SET @使用者變數 = 值;
SET @使用者變數 := 值;

#方式2:“:=” 或 INTO關鍵字
SELECT @使用者變數 := 表示式 [FROM 等子句];
SELECT 表示式 INTO @使用者變數  [FROM 等子句];

  • 檢視使用者變數的值 (檢視、比較、運算等)
SELECT @使用者變數
  • 舉例
SET @a = 1;

SELECT @a;
SELECT @num := COUNT(*) FROM employees;

SELECT @num;
SELECT AVG(salary) INTO @avgsalary FROM employees;

SELECT @avgsalary;
SELECT @big;  #檢視某個未宣告的變數時,將得到NULL值

區域性變數

定義:可以使用DECLARE語句定義一個區域性變數

作用域:僅僅在定義它的 BEGIN ... END 中有效

位置:只能放在 BEGIN ... END 中,而且只能放在第一句

BEGIN
	#宣告區域性變數
	DECLARE 變數名1 變數資料型別 [DEFAULT 變數預設值];
	DECLARE 變數名2,變數名3,... 變數資料型別 [DEFAULT 變數預設值];

	#為區域性變數賦值
	SET 變數名1 = 值;
	SELECT 值 INTO 變數名2 [FROM 子句];

	#檢視區域性變數的值
	SELECT 變數1,變數2,變數3;
END

1.定義變數

DECLARE 變數名 型別 [default 值];  # 如果沒有DEFAULT子句,初始值為NULL

舉例:

DECLARE myparam INT DEFAULT 100;

2.變數賦值

方式1:一般用於賦簡單的值

SET 變數名=值;
SET 變數名:=值;

方式2:一般用於賦表中的欄位值

SELECT 欄位名或表示式 INTO 變數名 FROM 表;

3.使用變數(檢視、比較、運算等)

SELECT 區域性變數名;

舉例1:宣告區域性變數,並分別賦值為employees表中employee_id為102的last_name和salary

DELIMITER //

CREATE PROCEDURE set_value()
BEGIN
	DECLARE emp_name VARCHAR(25);
	DECLARE sal DOUBLE(10,2);
	
	SELECT last_name,salary INTO emp_name,sal
	FROM employees 
	WHERE employee_id = 102;
	
	SELECT emp_name,sal;
END //

DELIMITER ;

舉例2:宣告兩個變數,求和並列印 (分別使用會話使用者變數、區域性變數的方式實現)

#方式1:使用使用者變數
SET @m=1;
SET @n=1;
SET @sum=@m+@n;

SELECT @sum;
#方式2:使用區域性變數
DELIMITER //

CREATE PROCEDURE add_value()
BEGIN
	#區域性變數
	DECLARE m INT DEFAULT 1;
	DECLARE n INT DEFAULT 3;
	DECLARE SUM INT;
	
	SET SUM = m+n;
	SELECT SUM;
END //

DELIMITER ;

舉例3:建立儲存過程“different_salary”查詢某員工和他領導的薪資差距,並用IN引數emp_id接收員工id,用OUT引數dif_salary輸出薪資差距結果。

#宣告
DELIMITER //
	
CREATE PROCEDURE different_salary(IN emp_id INT,OUT dif_salary DOUBLE)
BEGIN
	#宣告區域性變數
	DECLARE emp_sal,mgr_sal DOUBLE DEFAULT 0.0;
	DECLARE mgr_id INT;
	
	SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id;
	SELECT manager_id INTO mgr_id FROM employees WHERE employee_id = emp_id;
	SELECT salary INTO mgr_sal FROM employees WHERE employee_id = mgr_id;
	SET dif_salary = mgr_sal - emp_sal;

END //

DELIMITER ;

#呼叫
SET @emp_id = 102;
CALL different_salary(@emp_id,@diff_sal);


#檢視
SELECT @diff_sal;

對比會話使用者變數與區域性變數

作用域 定義位置 語法
會話使用者變數 當前會話 會話的任何地方 加@符號,不用指定型別
區域性變數 定義它的BEGIN END中 BEGIN END的第一句話 一般不用加@,需要指定型別

異常處理

定義條件是事先定義程式執行過程中可能遇到的問題,處理程式定義了在遇到問題時應當採取的處理方式,並且保證儲存過程或函式在遇到警告或錯誤時能繼續執行。這樣可以增強儲存程式處理問題的能力,避免程式異常停止執行。

說明:定義條件和處理程式在儲存過程、儲存函式中都是支援的。

案例分析

案例分析:建立一個名稱為“UpdateDataNoCondition”的儲存過程。程式碼如下:

DELIMITER //

CREATE PROCEDURE UpdateDataNoCondition()
	BEGIN
		SET @x = 1;
		UPDATE employees SET email = NULL WHERE last_name = 'Abel';
		SET @x = 2;
		UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel';
		SET @x = 3;
	END //

DELIMITER ;

呼叫儲存過程:

mysql> CALL UpdateDataNoCondition();
ERROR 1048 (23000): Column 'email' cannot be null

mysql> SELECT @x;
+------+
| @x   |
+------+
|   1  |
+------+
1 row in set (0.00 sec)

可以看到,此時@x變數的值為1。結合建立儲存過程的SQL語句程式碼可以得出:在儲存過程中未定義條件和處理程式,且當儲存過程中執行的SQL語句報錯時,MySQL資料庫會丟擲錯誤,並退出當前SQL邏輯,不再向下繼續執行。

定義條件

定義條件就是給MySQL中的錯誤碼命名,這有助於儲存的程式程式碼更清晰。它將一個錯誤名字指定的錯誤條件關聯起來。這個名字可以隨後被用在定義處理程式的DECLARE HANDLER語句中。

定義條件使用DECLARE語句,語法格式如下:

DECLARE 錯誤名稱 CONDITION FOR 錯誤碼(或錯誤條件)

錯誤碼的說明:

  • MySQL_error_codesqlstate_value都可以表示MySQL的錯誤。
    • MySQL_error_code是數值型別錯誤程式碼。
    • sqlstate_value是長度為5的字串型別錯誤程式碼。
  • 例如,在ERROR 1418 (HY000)中,1418是MySQL_error_code,'HY000'是sqlstate_value。
  • 例如,在ERROR 1142(42000)中,1142是MySQL_error_code,'42000'是sqlstate_value。

舉例1:定義“Field_Not_Be_NULL”錯誤名與MySQL中違反非空約束的錯誤型別是“ERROR 1048 (23000)”對應。

#使用MySQL_error_code
DECLARE Field_Not_Be_NULL CONDITION FOR 1048;

#使用sqlstate_value
DECLARE Field_Not_Be_NULL CONDITION FOR SQLSTATE '23000';

舉例2:定義"ERROR 1148(42000)"錯誤,名稱為command_not_allowed。

#使用MySQL_error_code
DECLARE command_not_allowed CONDITION FOR 1148;

#使用sqlstate_value
DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000';

定義處理程式

可以為SQL執行過程中發生的某種型別的錯誤定義特殊的處理程式。定義處理程式時,使用DECLARE語句的語法如下:

DECLARE 處理方式 HANDLER FOR 錯誤型別 處理語句
  • 處理方式:處理方式有3個取值:CONTINUE、EXIT、UNDO。
    • CONTINUE:表示遇到錯誤不處理,繼續執行。
    • EXIT:表示遇到錯誤馬上退出。
    • UNDO:表示遇到錯誤後撤回之前的操作。MySQL中暫時不支援這樣的操作。
  • 錯誤型別(即條件)可以有如下取值:
    • SQLSTATE '字串錯誤碼':表示長度為5的sqlstate_value型別的錯誤程式碼;
    • MySQL_error_code:匹配數值型別錯誤程式碼;
    • 錯誤名稱:表示DECLARE ... CONDITION定義的錯誤條件名稱。
    • SQLWARNING:匹配所有以01開頭的SQLSTATE錯誤程式碼;
    • NOT FOUND:匹配所有以02開頭的SQLSTATE錯誤程式碼;
    • SQLEXCEPTION:匹配所有沒有被SQLWARNING或NOT FOUND捕獲的SQLSTATE錯誤程式碼;
  • 處理語句:如果出現上述條件之一,則採用對應的處理方式,並執行指定的處理語句。語句可以是像“SET 變數 = 值”這樣的簡單語句,也可以是使用BEGIN ... END編寫的複合語句。

定義處理程式的幾種方式,程式碼如下:

#方法1:捕獲sqlstate_value
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info = 'NO_SUCH_TABLE';

#方法2:捕獲mysql_error_value
DECLARE CONTINUE HANDLER FOR 1146 SET @info = 'NO_SUCH_TABLE';

#方法3:先定義條件,再呼叫
DECLARE no_such_table CONDITION FOR 1146;
DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info = 'NO_SUCH_TABLE';

#方法4:使用SQLWARNING
DECLARE EXIT HANDLER FOR SQLWARNING SET @info = 'ERROR';

#方法5:使用NOT FOUND
DECLARE EXIT HANDLER FOR NOT FOUND SET @info = 'NO_SUCH_TABLE';

#方法6:使用SQLEXCEPTION
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info = 'ERROR';

案例解決

在儲存過程中,定義處理程式,捕獲sqlstate_value值,當遇到MySQL_error_code值為1048時,執行CONTINUE操作,並且將@proc_value的值設定為-1。

DELIMITER //

CREATE PROCEDURE UpdateDataNoCondition()
	BEGIN
		#定義處理程式
		DECLARE CONTINUE HANDLER FOR 1048 SET @proc_value = -1;
		
		SET @x = 1;
		UPDATE employees SET email = NULL WHERE last_name = 'Abel';
		SET @x = 2;
		UPDATE employees SET email = 'aabbel' WHERE last_name = 'Abel';
		SET @x = 3;
	END //

DELIMITER ;

呼叫過程:

mysql> CALL UpdateDataWithCondition();
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT @x,@proc_value;
+------+-------------+
| @x   | @proc_value |
+------+-------------+
|    3 |       	 -1  |
+------+-------------+
1 row in set (0.00 sec)

舉例:

建立一個名稱為“InsertDataWithCondition”的儲存過程,程式碼如下。

在儲存過程中,定義處理程式,捕獲sqlstate_value值,當遇到sqlstate_value值為23000時,執行EXIT操作,並且將@proc_value的值設定為-1。

#準備工作
CREATE TABLE departments
AS
SELECT * FROM atguigudb.`departments`;

ALTER TABLE departments
ADD CONSTRAINT uk_dept_name UNIQUE(department_id);
DELIMITER //

CREATE PROCEDURE InsertDataWithCondition()
	BEGIN
		DECLARE duplicate_entry CONDITION FOR SQLSTATE '23000' ;
		DECLARE EXIT HANDLER FOR duplicate_entry SET @proc_value = -1;
		
		SET @x = 1;
		INSERT INTO departments(department_name) VALUES('測試');
		SET @x = 2;
		INSERT INTO departments(department_name) VALUES('測試');
		SET @x = 3;
	END //

DELIMITER ;

呼叫儲存過程:

mysql> CALL InsertDataWithCondition();
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT @x,@proc_value;
+------+-------------+
| @x   | @proc_value |
+------+-------------+
|    2 |       	 -1  |
+------+-------------+
1 row in set (0.00 sec)