【原創】mysql 錯誤緩沖堆棧

什麼是錯誤緩沖堆棧呢? 舉個很簡單的例子,比如執行下面一條語句:

mysql>INSERTINTOt_datetimeVALUES(2,'4','5');
ERROR1292(22007):Incorrectdatetimevalue:'4'forcolumn'log_time'atrow1

上面1292這個代碼指示的錯誤信息保存在哪裡呢? 就保存在錯誤緩沖堆棧, 在MySQL裡面叫 DIAGNOSTICS AREA。 關於這個概念,一直在MySQL5.7才得到確定的更新。

在MySQL5.5之前,想要得到這塊區域的數據,就隻能通過C的API來獲取,從SQL層面是無法檢索到的。MySQL5.5 先推出瞭這個概念。

在MySQL5.6發佈後,不但可以檢索這塊區域,而且還可以重新封裝,得到我們想要的數據。但是這塊區域依然是隻能保存一次錯誤代碼,很容易被重置。

在MySQL5.7發佈後,可以更加容易的檢索這塊區域,而且把這裡的數據放到一個STACK裡,重置的條件更加寬松。以下舉例子來說明。

示例表結構如下,

CREATETABLE`t_datetime`(
`id`int(11)NOTNULL,
`log_time`timestampNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,
`end_time`datetimeNOTNULL,
PRIMARYKEY(`id`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8;

用來記錄錯誤數據的日志表。

CREATETABLEtb_log(errornoint,errortextTEXT,error_timestampDATETIME);

在MySQL5.6環境下,我要這樣寫一段繁雜的代碼來獲取錯誤信息。

DELIMITER$$
USE`new_feature`$$
DROPPROCEDUREIFEXISTS`sp_do_insert`$$
CREATEDEFINER=`root`@`localhost`PROCEDURE`sp_do_insert`(
INf_idINT,
INf_log_timeVARCHAR(255),
INf_end_timeVARCHAR(255)
)
BEGIN
DECLAREdone1TINYINTDEFAULT0;--保存是否發生異常的佈爾值。
DECLAREiTINYINTDEFAULT1;
DECLAREv_errcountINTDEFAULT0;--獲取一次錯誤數據條數
DECLAREv_errnoINTDEFAULT0;--獲取錯誤代碼
DECLAREv_msgTEXT;--獲取錯誤詳細信息
DECLARECONTINUEHANDLERFORSQLEXCEPTION--設定一個異常處理塊
BEGIN
SETdone1=1;--發生異常,設置為1.
getdiagnosticsv_errcount=number;
SETv_msg='';
WHILEi<=v_errcount
DO
GETDIAGNOSTICSCONDITIONi
v_errno=MYSQL_ERRNO,v_msg=MESSAGE_TEXT;
SET@stmt=CONCAT('select',v_errno,',"',v_msg,'","',NOW(),'"into@errno',i,',@msg',i,',@log_timestamp',i,';');
PREPAREs1FROM@stmt;
EXECUTEs1;
SETi=i+1;
ENDWHILE;
DROPPREPAREs1;
END;
INSERTINTOt_datetime(id,log_time,end_time)VALUES(f_id,f_log_time,f_end_time);
IFdone1=1THEN--把錯誤數據記錄到表tb_log裡。
SETi=1;
WHILEi<=v_errcount
DO
SET@stmt=CONCAT('insertintotb_log');
SET@stmt=CONCAT(@stmt,'select@errno',i,',@msg',i,',@log_timestamp');
PREPAREs1FROM@stmt;
EXECUTEs1;
SETi=i+1;
ENDWHILE;
DROPPREPAREs1;
ENDIF;
END$$
DELIMITER;

 

MySQL5.7發佈後,現在可以精簡我的代碼瞭。

DELIMITER$$
USE`new_feature`$$
DROPPROCEDUREIFEXISTS`sp_do_insert`$$
CREATEDEFINER=`root`@`localhost`PROCEDURE`sp_do_insert`(
INf_idINT,
INf_log_timeVARCHAR(255),
INf_end_timeVARCHAR(255)
)
BEGIN
DECLAREiTINYINTDEFAULT1;
DECLAREv_errcountINTDEFAULT0;--獲取一次錯誤數據條數
DECLAREv_errnoINTDEFAULT0;--獲取錯誤代碼
DECLAREv_msgTEXT;--獲取錯誤詳細信息
DECLARECONTINUEHANDLERFORSQLEXCEPTION--設定一個異常處理塊
BEGIN
getstackeddiagnosticsv_errcount=number;
WHILEi<=v_errcount
DO
GETstackedDIAGNOSTICSCONDITIONi--把錯誤數據分別保存在變量裡
v_errno=MYSQL_ERRNO,v_msg=MESSAGE_TEXT;
INSERTINTOtb_logVALUES(v_errno,v_msg,NOW());
SETi=i+1;
ENDWHILE;
END;
INSERTINTOt_datetime(id,log_time,end_time)VALUES(f_id,f_log_time,f_end_time);
END$$
DELIMITER;

 

現在來執行下:

mysql>callsp_do_insert(2,'4','5');
QueryOK,1rowaffected(0.01sec)

 

來檢索表tb_log的數據。

mysql>select*fromtb_log\G
***************************1.row***************************
errorno:1265
errortext:Datatruncatedforcolumn'log_time'atrow1
error_timestamp:2015-11-1711:53:10
***************************2.row***************************
errorno:1265
errortext:Datatruncatedforcolumn'end_time'atrow1
error_timestamp:2015-11-1711:53:10
***************************3.row***************************
errorno:1062
errortext:Duplicateentry'2'forkey'PRIMARY'
error_timestamp:2015-11-1711:53:10
3rowsinset(0.00sec)

總結下, 如果先用到DIAGNOSTICS AREA, 最好是在存儲過程裡面寫代碼封裝SQL。

You May Also Like