自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MySQL存儲(chǔ)過(guò)程中的錯(cuò)誤處理

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

本文主要基于一篇 MySQL Tutorial的文章,同時(shí)補(bǔ)充了自己的幾個(gè)實(shí)踐。

概述

我們?cè)趫?zhí)行普通的 MySQL SQL 語(yǔ)句的時(shí)候,都會(huì)在某些情況下遇到錯(cuò)誤。比如,我們向一張表中插入一條已經(jīng)存在的記錄,導(dǎo)致了主鍵重復(fù),會(huì)出現(xiàn)如下的錯(cuò)誤:

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

  1. MySQL 錯(cuò)誤碼:就是 ERROR 后面的 1062, MySQL 自定義的錯(cuò)誤代碼,跟其他數(shù)據(jù)庫(kù)不通用。
  2. SQLSTATE 代碼:就是 錯(cuò)誤碼后面的 (23000)。五位字符,從 ANSI SQL和 ODBC 來(lái)的標(biāo)準(zhǔn)化的錯(cuò)誤代碼,跟錯(cuò)誤碼之間并沒(méi)有一一對(duì)應(yīng)的關(guān)系。

MySQL 的錯(cuò)誤碼和 SQLSTATE 的具體信息可參見(jiàn)官方手冊(cè):Server Error Codes and Messages

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

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

聲明一個(gè)錯(cuò)誤處理器

聲明一個(gè)錯(cuò)誤處理器所需的 DECLARE HANDLER 語(yǔ)句格式如下:

  1. DECLARE action HANDLER FOR condition_value statement; 

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

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

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

condition_value 指定了會(huì)激活錯(cuò)誤處理器的一個(gè)特定的條件或者一類錯(cuò)誤條件。其取值可以是:

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

statement 則可以是個(gè)簡(jiǎn)單的語(yǔ)句或者被 BEGIN 和 END 圍起來(lái)的多條語(yǔ)句。

MySQL 錯(cuò)誤處理器示例

我們先看幾個(gè)聲明錯(cuò)誤處理器的例子:

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

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

下面是另一個(gè)錯(cuò)誤處理器,指出如果發(fā)生任何錯(cuò)誤就回滾之前的操作,給出一條錯(cuò)誤信息,并退出當(dāng)前代碼塊的執(zhí)行。如果你是在聲明存儲(chǔ)過(guò)程的 BEGIN 和 END 語(yǔ)句之間聲明的這個(gè)錯(cuò)誤處理器,那么出錯(cuò)時(shí)會(huì)立即結(jié)束整個(gè)存儲(chǔ)過(guò)程的執(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 

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

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

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

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

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

存儲(chǔ)過(guò)程中出錯(cuò)處理的示例

首先為了演示我們創(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 之間的關(guān)系。每個(gè) Article 可以對(duì)應(yīng)到多個(gè) Tag,反過(guò)來(lái)也是一樣。為簡(jiǎn)單起見(jiàn),我們就不創(chuàng)建 article 和 tag 表了,因此也就不用給 article_tags 加外鍵約束了。

接下來(lái),我們創(chuàng)建一個(gè)存儲(chǔ)過(guò)程來(lái)把 Article 的 id 和 Tag 的 id 插入到 article_tags 表中,注意這個(gè)存儲(chǔ)過(guò)程***一條語(yǔ)句返回了表中的記錄總數(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 $$  

然后,通過(guò)下面的命令,我們給 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);  

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

  1. CALL insert_article_tags(1,3); 

 

 我們得到了一條錯(cuò)誤信息。不過(guò)因?yàn)槲覀兟暶鞯氖?CONTINUE 類型的錯(cuò)誤處理器,存儲(chǔ)過(guò)程會(huì)繼續(xù)往后執(zhí)行,所以結(jié)果我們也得到了 article 的 tag 的總數(shù)。

如果我們把 CONTINUE 類型的錯(cuò)誤處理器聲明成 EXIT,我們就只得到一個(gè)錯(cuò)誤提示了。讓我們?cè)倬帉?xiě)另外一個(gè)存儲(chǔ)過(guò)程:

  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 $$  

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

  1. CALL insert_article_tags_2(1,3); 

 

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

錯(cuò)誤處理器的優(yōu)先級(jí)

當(dāng)有多個(gè)錯(cuò)誤處理器都滿足特定錯(cuò)誤條件的時(shí)候,MySQL將按更明確者優(yōu)先的原則決定優(yōu)先級(jí)。

MySQL中的每個(gè)錯(cuò)誤都會(huì)映射到一個(gè)特定的錯(cuò)誤碼,因此錯(cuò)誤碼是最明確的。一個(gè) SQLSTATE 可以對(duì)應(yīng)到多個(gè) MySQL 錯(cuò)誤碼,所以沒(méi)那么明確。SQLEXCEPTION 和 SQLWARNING 分別指代的是 SQLSTATES 中類型相近的一組值,所以它的明確性***。

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

如果我們?cè)?insert_article_tags_3 存儲(chǔ)過(guò)程里聲明3個(gè)錯(cuò)誤處理器,像下面一樣:

  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 $$  

當(dāng)我們通過(guò)下面的命令,試圖調(diào)用上面的存儲(chǔ)過(guò)程插入一條主鍵重復(fù)的記錄到article_tags表時(shí):

  1. CALL insert_article_tags_3(1,3); 

 

 你會(huì)看到, MySQL錯(cuò)誤碼綁定的那個(gè)處理器被調(diào)用了。

使用命名錯(cuò)誤條件

我們先看一個(gè)錯(cuò)誤處理器的聲明:

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

1051 這個(gè)錯(cuò)誤碼到底什么意思?想象一下如果你有很多的存儲(chǔ)過(guò)程,里面散布者這種數(shù)字,對(duì)于代碼維護(hù)來(lái)說(shuō)應(yīng)該就是噩夢(mèng)了。

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

DECLARE CONDITION 語(yǔ)句的語(yǔ)法如下所示:

  1. DECLARE condition_name CONDITION FOR condition_value; 

condition_value 可以是一個(gè)類似 1051 的 MySQL 錯(cuò)誤碼,或者一個(gè) SQLSTATE 值,然后 condition_name 就可以代表condition_value 來(lái)使用了。

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

  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;  

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

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

實(shí)際應(yīng)用中,存儲(chǔ)過(guò)程中的錯(cuò)誤被我們的錯(cuò)誤處理器捕獲了之后,你如果還想用類似 mysql 命令行那樣的格式返回對(duì)應(yīng)的錯(cuò)誤,可以聲明一個(gè)這樣的函數(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$$  

那么在實(shí)際使用時(shí),就可以在錯(cuò)誤處理器中這樣用:

  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 

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

 

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2009-07-23 14:10:38

Hibernate J

2011-08-15 15:56:31

SQL Server

2010-05-27 17:45:13

MySQL存儲(chǔ)過(guò)程

2010-05-31 16:57:09

2023-12-26 22:05:53

并發(fā)代碼goroutines

2010-05-27 17:56:39

MySQL存儲(chǔ)過(guò)程

2023-10-28 16:30:19

Golang開(kāi)發(fā)

2010-11-26 16:18:13

MySQL變量定義

2021-04-14 07:08:14

Nodejs錯(cuò)誤處理

2011-04-11 17:28:50

oracle存儲(chǔ)select語(yǔ)句

2024-03-27 08:18:02

Spring映射HTML

2024-09-23 08:10:00

.NET開(kāi)發(fā)

2014-11-17 10:05:12

Go語(yǔ)言

2023-10-26 12:05:14

Golang開(kāi)發(fā)

2021-04-29 09:02:44

語(yǔ)言Go 處理

2022-08-26 16:28:41

MySQL存儲(chǔ)只讀語(yǔ)句

2010-04-15 16:54:31

Oracle存儲(chǔ)過(guò)程

2010-11-12 09:18:13

SQL Server存

2025-02-10 09:49:00

2017-12-19 07:09:22

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

51CTO技術(shù)棧公眾號(hào)