偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

MySQL存儲過程中的錯誤處理

數(shù)據(jù)庫 MySQL
今天我們要講的是如果在存儲過程中遇到錯誤應該如何處理。因為存儲過程中是很多條 SQL 語句構成的 FUNCTION 或者 PROCEDURE,發(fā)生的錯誤通常會影響代碼的執(zhí)行,因此妥善的處理錯誤是很重要的,比如繼續(xù)或者退出執(zhí)行當前代碼塊,并給出一個容易理解的錯誤信息。

本文主要基于一篇 MySQL Tutorial的文章,同時補充了自己的幾個實踐。

概述

我們在執(zhí)行普通的 MySQL SQL 語句的時候,都會在某些情況下遇到錯誤。比如,我們向一張表中插入一條已經(jīng)存在的記錄,導致了主鍵重復,會出現(xiàn)如下的錯誤:

 上圖中標記為紅色的部分,就是 MySQL 返回的錯誤信息在 mysql 命令行客戶端中的提示。里面有兩個值得注意的部分:

  1. MySQL 錯誤碼:就是 ERROR 后面的 1062, MySQL 自定義的錯誤代碼,跟其他數(shù)據(jù)庫不通用。
  2. SQLSTATE 代碼:就是 錯誤碼后面的 (23000)。五位字符,從 ANSI SQL和 ODBC 來的標準化的錯誤代碼,跟錯誤碼之間并沒有一一對應的關系。

MySQL 的錯誤碼和 SQLSTATE 的具體信息可參見官方手冊:Server Error Codes and Messages

今天我們要講的是如果在存儲過程中遇到錯誤應該如何處理。因為存儲過程中是很多條 SQL 語句構成的 FUNCTION 或者PROCEDURE,發(fā)生的錯誤通常會影響代碼的執(zhí)行,因此妥善的處理錯誤是很重要的,比如繼續(xù)或者退出執(zhí)行當前代碼塊,并給出一個容易理解的錯誤信息。

MySQL 提供了一個簡單的手段,即定義錯誤處理器(Handler),來捕獲從通用的警告或者異常,到更具體的錯誤碼等各種錯誤條件。

聲明一個錯誤處理器

聲明一個錯誤處理器所需的 DECLARE HANDLER 語句格式如下:

  1. DECLARE action HANDLER FOR condition_value statement; 

如果一個錯誤條件的值符合 condition_value,MySQL 就會執(zhí)行對應的 statement,并根據(jù) action 指定關鍵字確定是 繼續(xù) 還是退出 當前的代碼塊(譯者注,當前代碼塊就是包含此錯誤處理器的最近的那對 BEGIN 和 END圍出來的代碼段)。

參數(shù) action 可以取以下兩個值:

  • CONTINUE : 當前代碼段會從出錯的地方繼續(xù)執(zhí)行。
  • EXIT : 當前代碼段從出錯的地方終止執(zhí)行。

condition_value 指定了會激活錯誤處理器的一個特定的條件或者一類錯誤條件。其取值可以是:

  • 一個 MySQL 錯誤碼
  • 一個標準的 SQLSTATE 值?;蛘呖梢允?SQLWARNING , SQLEXCEPTION 等條件,這些分別代表一組類似的 SQLSTATE值。NOTFOUND 條件則可用于游標或者 SELECT INTO variable_list 語句,表示沒有找到匹配的數(shù)據(jù)行。
  • 一個與特定 MySQL 錯誤代碼或者 SQLSTATE 值關聯(lián)的命名條件,說白了就是個別名。

statement 則可以是個簡單的語句或者被 BEGIN 和 END 圍起來的多條語句。

MySQL 錯誤處理器示例

我們先看幾個聲明錯誤處理器的例子:

下面的處理器指出:如果發(fā)生了錯誤,就將 has_error 變量的值設為 1 并繼續(xù)執(zhí)行出錯的語句所在的代碼塊。

  1. DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET has_error = 1; 

下面是另一個錯誤處理器,指出如果發(fā)生任何錯誤就回滾之前的操作,給出一條錯誤信息,并退出當前代碼塊的執(zhí)行。如果你是在聲明存儲過程的 BEGIN 和 END 語句之間聲明的這個錯誤處理器,那么出錯時會立即結束整個存儲過程的執(zhí)行。

  1. DECLARE EXIT HANDLER FOR SQLEXCEPTION 
  2. BEGIN 
  3. ROLLBACK
  4. SELECT 'An error has occurred, operation rollbacked and the stored procedure was terminated'
  5. END 

下面的錯誤處理器指出,如果有發(fā)生數(shù)據(jù)行不存在的錯誤,其實就是指在使用游標(Cursor)或者SELECT INTO語句的情況,就把no_row_found 變量設為1,并繼續(xù)執(zhí)行。

  1. DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_row_found = 1; 

譯者注:如果只是為了檢查 SELECT INTO 是否有行數(shù)據(jù)返回,MySQL 還有另外一個 FOUND_ROWS() 內置函數(shù)可以用。

下面的錯誤處理器指出,如果發(fā)生了主鍵重復的錯誤(MySQL的錯誤碼為1062),就將給出一條錯誤提示并繼續(xù)代碼塊的執(zhí)行:

  1. DECLARE CONTINUE HANDLER FOR 1062 
  2. SELECT 'Error, duplicate key occurred' 

存儲過程中出錯處理的示例

首先為了演示我們創(chuàng)建一張新表,表名是 article_tags:

  1. CREATE TABLE article_tags( 
  2.     article_id INT
  3.     tag_id     INT
  4.     PRIMARY KEY(article_id,tag_id) 
  5. );  

article_tags 表保存了 article 和 tag 之間的關系。每個 Article 可以對應到多個 Tag,反過來也是一樣。為簡單起見,我們就不創(chuàng)建 article 和 tag 表了,因此也就不用給 article_tags 加外鍵約束了。

接下來,我們創(chuàng)建一個存儲過程來把 Article 的 id 和 Tag 的 id 插入到 article_tags 表中,注意這個存儲過程***一條語句返回了表中的記錄總數(shù):

  1. DELIMITER $$ 
  2.  
  3. CREATE PROCEDURE insert_article_tags(IN article_id INTIN tag_id INT
  4. BEGIN 
  5.  
  6.     DECLARE CONTINUE HANDLER FOR 1062 
  7.     SELECT CONCAT('duplicate keys (',article_id,',',tag_id,') found'AS msg; 
  8.  
  9.     -- insert a new record into article_tags 
  10.     INSERT INTO article_tags(article_id,tag_id) 
  11.     VALUES(article_id,tag_id); 
  12.  
  13.     -- return tag count for the article 
  14.     SELECT COUNT(*) FROM article_tags; 
  15. END $$  

然后,通過下面的命令,我們給 Id=1 的 Article 新增 Id 為1,2,3 的 Tag:

  1. CALL insert_article_tags(1,1); 
  2. CALL insert_article_tags(1,2); 
  3. CALL insert_article_tags(1,3);  

這之后,我們就要嘗試插入一條重復的記錄來看看錯誤處理器是否真的會被執(zhí)行到。

  1. CALL insert_article_tags(1,3); 

 

 我們得到了一條錯誤信息。不過因為我們聲明的是 CONTINUE 類型的錯誤處理器,存儲過程會繼續(xù)往后執(zhí)行,所以結果我們也得到了 article 的 tag 的總數(shù)。

如果我們把 CONTINUE 類型的錯誤處理器聲明成 EXIT,我們就只得到一個錯誤提示了。讓我們再編寫另外一個存儲過程:

  1. DELIMITER $$ 
  2.  
  3. CREATE PROCEDURE insert_article_tags_2(IN article_id INTIN tag_id INT
  4. BEGIN 
  5.  
  6.     DECLARE EXIT HANDLER FOR SQLEXCEPTION  
  7.     SELECT 'SQLException invoked'
  8.  
  9.     DECLARE EXIT HANDLER FOR 1062  
  10.         SELECT 'MySQL error code 1062 invoked'
  11.  
  12.     DECLARE EXIT HANDLER FOR SQLSTATE '23000' 
  13.     SELECT 'SQLSTATE 23000 invoked'
  14.  
  15.     -- insert a new record into article_tags 
  16.     INSERT INTO article_tags(article_id,tag_id) 
  17.        VALUES(article_id,tag_id); 
  18.  
  19.     -- return tag count for the article 
  20.     SELECT COUNT(*) FROM article_tags; 
  21. END $$  

***,我們可以嘗試新增一條重復主鍵的記錄看看效果:

  1. CALL insert_article_tags_2(1,3); 

 

 可以看到這次只輸出了錯誤信息就沒有繼續(xù)向下執(zhí)行了。

錯誤處理器的優(yōu)先級

當有多個錯誤處理器都滿足特定錯誤條件的時候,MySQL將按更明確者優(yōu)先的原則決定優(yōu)先級。

MySQL中的每個錯誤都會映射到一個特定的錯誤碼,因此錯誤碼是最明確的。一個 SQLSTATE 可以對應到多個 MySQL 錯誤碼,所以沒那么明確。SQLEXCEPTION 和 SQLWARNING 分別指代的是 SQLSTATES 中類型相近的一組值,所以它的明確性***。

基于錯誤處理器的優(yōu)先級規(guī)則,MySQL 錯誤碼處理器,SQLSTATE 錯誤處理器 和 SQLEXCEPTION錯誤處理器順序上分別排在1、2、3位。

如果我們在 insert_article_tags_3 存儲過程里聲明3個錯誤處理器,像下面一樣:

  1. DELIMITER $$ 
  2.  
  3. CREATE PROCEDURE insert_article_tags_3(IN article_id INTIN tag_id INT
  4. BEGIN 
  5.  
  6.     DECLARE EXIT HANDLER FOR 1062 SELECT 'Duplicate keys error encountered'
  7.     DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT 'SQLException encountered'
  8.     DECLARE EXIT HANDLER FOR SQLSTATE '23000' SELECT 'SQLSTATE 23000'
  9.  
  10.     -- insert a new record into article_tags 
  11.     INSERT INTO article_tags(article_id,tag_id) 
  12.     VALUES(article_id,tag_id); 
  13.  
  14.     -- return tag count for the article 
  15.     SELECT COUNT(*) FROM article_tags; 
  16. END $$  

當我們通過下面的命令,試圖調用上面的存儲過程插入一條主鍵重復的記錄到article_tags表時:

  1. CALL insert_article_tags_3(1,3); 

 

 你會看到, MySQL錯誤碼綁定的那個處理器被調用了。

使用命名錯誤條件

我們先看一個錯誤處理器的聲明:

  1. DECLARE EXIT HANDLER FOR 1051 SELECT 'Please create table abc first'
  2. SELECT * FROM abc;  

1051 這個錯誤碼到底什么意思?想象一下如果你有很多的存儲過程,里面散布者這種數(shù)字,對于代碼維護來說應該就是噩夢了。

幸運的是,MySQL 為我們提供了一個 DECLARE CONDITION 語句來聲明一個命名錯誤條件,可以將上面的數(shù)字關聯(lián)為一個有意義的名字。

DECLARE CONDITION 語句的語法如下所示:

  1. DECLARE condition_name CONDITION FOR condition_value; 

condition_value 可以是一個類似 1051 的 MySQL 錯誤碼,或者一個 SQLSTATE 值,然后 condition_name 就可以代表condition_value 來使用了。

所以之前的代碼我們就可以改寫成下面的樣子:

  1. DECLARE table_not_found CONDITION for 1051; 
  2. DECLARE EXIT HANDLER FOR  table_not_found SELECT 'Please create table abc first'
  3. SELECT * FROM abc;  

這樣代碼的可讀性比之前明顯好多了。需要注意的是,要在存儲過程聲明一個命名條件的語句,則該語句必須出現(xiàn)在錯誤處理器或者游標聲明的前面。

一個在 handler 中實用的輔助函數(shù)

實際應用中,存儲過程中的錯誤被我們的錯誤處理器捕獲了之后,你如果還想用類似 mysql 命令行那樣的格式返回對應的錯誤,可以聲明一個這樣的函數(shù):

  1. DELIMITER $$ 
  2. CREATE FUNCTION fn_get_error() 
  3. RETURNS VARCHAR(250) 
  4. BEGIN 
  5.     DECLARE code CHAR(5) DEFAULT '00000'
  6.     DECLARE msg TEXT; 
  7.     DECLARE errno INT
  8.      
  9.     GET DIAGNOSTICS CONDITION 1 code = RETURNED_SQLSTATE,  
  10.         errno = MYSQL_ERRNO, msg = MESSAGE_TEXT; 
  11.      
  12.     RETURN COALESCE(CONCAT("ERROR ", errno, " (", code, "): ", msg), '-'); 
  13. END$$  

那么在實際使用時,就可以在錯誤處理器中這樣用:

  1. DECLARE EXIT HANDLER FOR SQLEXCEPTION  
  2.      BEGIN  
  3.          ROLLBACK;  
  4.          SET ret = -9;  
  5.          SELECT ret AS 'ret', fn_get_error() AS 'err'
  6.      END;         
  7.      
  8.     DECLARE EXIT HANDLER FOR 1062 
  9.     BEGIN 
  10.         ROLLBACK;  
  11.          SET ret = -1;  
  12.          SELECT ret AS 'ret', fn_get_error() AS 'err'
  13.     END 

那么在有錯誤發(fā)生的時候,就可以得到如下的錯誤提示了:

 

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2009-07-23 14:10:38

Hibernate J

2011-08-15 15:56:31

SQL Server

2010-05-31 16:57:09

2010-05-27 17:45:13

MySQL存儲過程

2023-12-26 22:05:53

并發(fā)代碼goroutines

2010-05-27 17:56:39

MySQL存儲過程

2010-11-26 16:18:13

MySQL變量定義

2023-10-28 16:30:19

Golang開發(fā)

2021-04-14 07:08:14

Nodejs錯誤處理

2011-04-11 17:28:50

oracle存儲select語句

2024-03-27 08:18:02

Spring映射HTML

2022-08-26 16:28:41

MySQL存儲只讀語句

2014-11-17 10:05:12

Go語言

2024-09-23 08:10:00

.NET開發(fā)

2010-04-15 16:54:31

Oracle存儲過程

2010-11-12 09:18:13

SQL Server存

2021-04-29 09:02:44

語言Go 處理

2023-10-26 12:05:14

Golang開發(fā)

2025-02-10 09:49:00

2017-12-19 07:09:22

數(shù)據(jù)中心合并IT
點贊
收藏

51CTO技術棧公眾號