I’ve created a MySQL function and would like to raise an error if the values passed for the parameters are invalid. What are my options for raising an error within a MySQL function?
dolmen
7,8425 gold badges34 silver badges42 bronze badges
asked Jan 21, 2009 at 15:22
MySQL 5.5 introduces signals, which are similar to exceptions in other languages:
http://dev.mysql.com/doc/refman/5.5/en/signal.html
For example, in the mysql
command line client:
mysql> SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Custom error';
ERROR 1644 (45000): Custom error
answered Aug 6, 2012 at 13:39
Austin HydeAustin Hyde
25.8k28 gold badges94 silver badges127 bronze badges
It’s actually a combination of all three answers. You call a non-existent procedure to raise the error, and then declare an exit handler that catches the error you generated. Here’s an example, using SQLSTATE 42000 (procedure does not exist) to throw an error before deletion if the row to be deleted has a foreign key id set:
DROP PROCEDURE IF EXISTS decount_test;
DELIMITER //
CREATE DEFINER = 'root'@'localhost' PROCEDURE decount_test ( p_id bigint )
DETERMINISTIC MODIFIES SQL DATA
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '42000'
SELECT 'Invoiced barcodes may not have accounting removed.';
IF (SELECT invoice_id
FROM accounted_barcodes
WHERE id = p_id
) THEN
CALL raise_error;
END IF;
DELETE FROM accounted_barcodes WHERE id = p_id;
END //
DELIMITER ;
Output:
call decount_test(123456);
+----------------------------------------------------+
| Invoiced barcodes may not have accounting removed. |
+----------------------------------------------------+
| Invoiced barcodes may not have accounting removed. |
+----------------------------------------------------+
Kev
15.6k15 gold badges78 silver badges112 bronze badges
answered Apr 22, 2010 at 18:31
Ryan MRyan M
6886 silver badges12 bronze badges
2
Why not just store a VARCHAR
in a declared INTEGER
variable?
DELIMITER $$ DROP FUNCTION IF EXISTS `raise_error` $$
CREATE FUNCTION `raise_error`(MESSAGE VARCHAR(255))
RETURNS INTEGER DETERMINISTIC BEGIN
DECLARE ERROR INTEGER;
set ERROR := MESSAGE;
RETURN 0;
END $$ DELIMITER ;
-- set @foo := raise_error('something failed'); -- or within a query
Error message is:
Incorrect integer value: ‘something failed’ for column ‘ERROR’ at row
1
It’s not perfect, but it gives a pretty descriptive message and you don’t have to write any extension DLLs.
answered Feb 12, 2013 at 19:56
3
You can also call an existing function with an invalid number of arguments.
answered Jul 6, 2009 at 17:45
Andrew CharneskiAndrew Charneski
answered Jan 21, 2009 at 20:24
1
In this tutorial, we will learn how to raise an error using the signal statement. The SIGNAL and RESIGNAL statements are very similar to each other except few points. We will see the details and examples of the SIGNAL statement in this article and for the RESIGNAL statement, we will have a separate tutorial.
Also read: MySQL Index Hints- FORCE INDEX
Introduction to MySQL SIGNAL
The SIGNAL statement is used to throw an error in the stored programs such as stored procedures, stored programs, triggers or events. Using the SIGNAL, you can return an error number, SQL STATE value or a custom message.
To execute the SIGNAL statement, you don’t need any special privilege.
In short, you can use the SIGNAL statement to return a custom message when an error occurs in the program.
Also read: Variables in MySQL Stored Procedures – Easy Implementation
MySQL SIGNAL Statement Syntax
The following syntax shows the correct way to use the SIGNAL statement-
Code language: SQL (Structured Query Language) (sql)
SIGNAL condition_value [SET signal_information_item [, signal_information_item] ...] condition_value: { SQLSTATE [VALUE] sqlstate_value | condition_name } signal_information_item: condition_information_item_name = simple_value_specification
Here,
The condition_value tells the error value to be thrown. The condtion_value can be an SQLSTATE or a condition_name which refers to the previously created named definition with the DECLARE … CONDITION statement.
The SQLSTATE value for a SIGNAL statement shouldn’t begin with the number “00” because these values imply success rather than an error. Instead, they should begin with a value other than ’00’. Either the SQLSTATE value is explicitly provided in the SIGNAL statement or it is referenced by a named condition in the statement, but either way, it is true. A Bad SQLSTATE problem manifests itself when the value is incorrect.
Optionally, the SIGNAL statement includes a SET clause with a list of condition information item name = simple_value_specification assignments, separated by commas, for each signal item.
Use the code “45000,” which stands for “unhandled user-defined exception,” to indicate a generic SQLSTATE value.
MySQL SIGNAL Statement Examples
Now we will take some examples to demonstrate the use of the SIGNAL statement.
Here, we will create a procedure to show the use of warnings and errors with the signal statement.
DROP PROCEDURE IF EXISTS signalDemo; DELIMITER // CREATE PROCEDURE signalDemo(num INT) BEGIN IF num = 0 THEN SIGNAL SQLSTATE '01000'; ELSEIF num = 1 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT="error raised 1"; ELSEIF num = 2 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT="error raised 2"; ELSE SIGNAL SQLSTATE '01000' SET MESSAGE_TEXT="warning raised" , MYSQL_ERRNO = 1000; END IF; END; // DELIMITER ;
Code language: SQL (Structured Query Language) (sql)
Here, if the value of num is 0, the procedure will signal a warning because the SQLSTATE value that starts with ’01’ is the warning.
Note that, the warning doesn’t terminate the procedure.
When the value of num is 1 or 2, the procedure signals an error and sets the message_text item. Here, after the error is caught, the procedure terminates and the text that you set earlier will be displayed.
If the value of num is anything else, the procedure signals the warning and sets the custom message with the error number.
Let’s examine everything by calling the procedures.
Code language: SQL (Structured Query Language) (sql)
CALL signalDemo(0);
Code language: SQL (Structured Query Language) (sql)
CALL signalDemo(1); CALL signalDemo(2);
Code language: SQL (Structured Query Language) (sql)
CALL signalDemo(3);
A specific SQLSTATE value is used to signal the condition if the SIGNAL statement specifies that value.
Let’s take an example of divide by zero error.
Code language: SQL (Structured Query Language) (sql)
DROP PROCEDURE IF EXISTS div_by_zero; DELIMITER // CREATE PROCEDURE div_by_zero(divisor INT) BEGIN IF divisor = 0 THEN SIGNAL SQLSTATE '22012'; END IF; END; // DELIMITER ;
You can also give them a name to the signal statement. If you do so, the condition must be defined using the SQLSTATE value instead of the MySQL error number.
Let’s re-write the above procedure using the named signal statement.
Code language: SQL (Structured Query Language) (sql)
DROP PROCEDURE IF EXISTS div_by_zero; DELIMITER // CREATE PROCEDURE div_by_zero(divisor INT) BEGIN DECLARE div_by_zero_error CONDITION FOR SQLSTATE '22012'; IF divisor = 0 THEN SIGNAL div_by_zero_error; END IF; END; // DELIMITER ;
Summary
In this tutorial, we have learned about the SIGNAL statement in MySQL. It is a broad topic to learn and understand. However, it is one of the most important topics in MySQL. It is absolutely worth learning the SIGNAL topic. If you want to get a more detailed view of the SIGNAL, you can check out its official documentation.
References
MySQL official documentation on the SIGNAL.
Summary: in this tutorial, you will learn how to use SIGNAL
and RESIGNAL
statements to raise error conditions inside stored procedures.
MySQL SIGNAL statement
You use the SIGNAL
statement to return an error or warning condition to the caller from a stored program e.g., stored procedure, stored function, trigger or event. The SIGNAL
statement provides you with control over which information for returning such as value and messageSQLSTATE
.
The following illustrates syntax of the SIGNAL
statement:
SIGNAL SQLSTATE | condition_name; SET condition_information_item_name_1 = value_1, condition_information_item_name_1 = value_2, etc;
Code language: SQL (Structured Query Language) (sql)
Following the SIGNAL
keyword is a SQLSTATE
value or a condition name declared by the DECLARE CONDITION
statement. Notice that the SIGNAL
statement must always specify a SQLSTATE
value or a named condition that defined with an SQLSTATE
value.
To provide the caller with information, you use the SET
clause. If you want to return multiple condition information item names with values, you need to separate each name/value pair by a comma.
The condition_information_item_name
can be MESSAGE_TEXT
, MYSQL_ERRORNO
, CURSOR_NAME
, etc.
The following stored procedure adds an order line item into an existing sales order. It issues an error message if the order number does not exist.
Code language: SQL (Structured Query Language) (sql)
DELIMITER $$ CREATE PROCEDURE AddOrderItem( in orderNo int, in productCode varchar(45), in qty int, in price double, in lineNo int ) BEGIN DECLARE C INT; SELECT COUNT(orderNumber) INTO C FROM orders WHERE orderNumber = orderNo; -- check if orderNumber exists IF(C != 1) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Order No not found in orders table'; END IF; -- more code below -- ... END
First, it counts the orders with the input order number that we pass to the stored procedure.
Second, if the number of order is not 1, it raises an error with SQLSTATE 45000
along with an error message saying that order number does not exist in the orders table.
Notice that 45000
is a generic SQLSTATE
value that illustrates an unhandled user-defined exception.
If we call the stored procedure AddOrderItem()
and pass a nonexistent order number, we will get an error message.
CALL AddOrderItem(10,'S10_1678',1,95.7,1);
Code language: JavaScript (javascript)
MySQL RESIGNAL statement
Besides the SIGNAL
statement, MySQL also provides the RESIGNAL
statement used to raise a warning or error condition.
The RESIGNAL
statement is similar to SIGNAL
statement in term of functionality and syntax, except that:
- You must use the
RESIGNAL
statement within an error or warning handler, otherwise, you will get an error message saying that “RESIGNAL when the handler is not active”. Notice that you can useSIGNAL
statement anywhere inside a stored procedure. - You can omit all attributes of the
RESIGNAL
statement, even theSQLSTATE
value.
If you use the RESIGNAL
statement alone, all attributes are the same as the ones passed to the condition handler.
The following stored procedure changes the error message before issuing it to the caller.
Code language: SQL (Structured Query Language) (sql)
DELIMITER $$ CREATE PROCEDURE Divide(IN numerator INT, IN denominator INT, OUT result double) BEGIN DECLARE division_by_zero CONDITION FOR SQLSTATE '22012'; DECLARE CONTINUE HANDLER FOR division_by_zero RESIGNAL SET MESSAGE_TEXT = 'Division by zero / Denominator cannot be zero'; -- IF denominator = 0 THEN SIGNAL division_by_zero; ELSE SET result := numerator / denominator; END IF; END
Let’s call the Divide()
stored procedure.
Code language: CSS (css)
CALL Divide(10,0,@result);
In this tutorial, we have shown you how to raise error conditions inside stored programs using SIGNAL
and RESIGNAL
statements.
Was this tutorial helpful?
message_text — сообщение, которое вы хотите показать при ошибке. Замечание: вы можете добавлять пользовательские сообщения для вывода информации об ошибке. Смотрите следующий раздел статьи.
message_id — id сообщения об ошибке. Если вы хотите вывести пользовательское сообщение, вы можете определить этот идентификатор. Посмотрите список идентификаторов сообщений в sys.messages DMV.
Запрос:
select * from sys.messages
Вывод:
severity — серьезность ошибки. Тип данных переменной severity — smallint, значения находятся в диапазоне от 0 до 25. Допустимыми значениями серьезности ошибки являются:
- 0-10
- 11-18
- 19-25
— информационные сообщения
— ошибки
— фатальные ошибки
Замечание: Если вы создаете пользовательское сообщение, сложность, указанная в этом сообщении, будет перебиваться сложностью, заданной в операторе RAISERROR.
state — уникальное идентификационное число, которое может использоваться для раздела кода, вызывающего ошибку. Тип данных параметра state — smallint, и допустимые значения между 0 и 255.
Теперь давайте перейдем к практическим примерам.
Пример 1: использование оператора SQL Server RAISERROR для вывода сообщения
В этом примере вы можете увидеть, как можно отобразить ошибку или информационное сообщение с помощью оператора RAISERROR.
Предположим, что вы хотите отобразить сообщение после вставки записей в таблицу. Мы можем использовать операторы PRINT или RAISERROR. Ниже — код:
SET nocount ON
INSERT INTO tblpatients
(patient_id,
patient_name,
address,
city)
VALUES ('OPD00006',
'Nimesh Upadhyay',
'AB-14, Ratnedeep Flats',
'Mehsana')
RAISERROR ( 'Patient detail added successfully',1,1)
Вывод:
Как видно на рисунке выше, ID сообщения равно 50000, поскольку это пользовательское сообщение.
Пример 2: оператор SQL RAISERROR с текстом динамического сообщения
Теперь посмотрите, как мы можем создать текст динамического сообщения для оператора SQL RAISERROR.
Предположим, что мы хотим напечатать в сообщении ID пациента. Я описал локальную переменную с именем @PatientID, которая содержит patient_id. Чтобы отобразить значение переменной @PatientID в тексте сообщения, мы можем использовать следующий код:
DECLARE @PatientID VARCHAR(15)
DECLARE @message NVARCHAR(max)
SET @PatientID='OPD00007'
SET @message ='Patient detail added successfully. The OPDID is %s'
INSERT INTO tblpatients
(patient_id,
patient_name,
address,
city)
VALUES ('' + @PatientID + '',
'Nimesh Upadhyay',
'AB-14, Ratnedeep Flats',
'Mehsana')
RAISERROR ( @message,1,1,@patientID)
Вывод:
Для отображения строки в операторе RAISERROR, мы должны использовать операторы print в стиле языка Си.
Как видно на изображении выше, для вывода параметра в тексте сообщения я использую опцию %s, которая отображает строковое значение параметра. Если вы хотите вывести целочисленный параметр, вы можете использовать опцию %d.
Использование SQL RAISERROR в блоке TRY..CATCH
В этом примере мы добавляем SQL RAISERROR в блок TRY. При запуске этого кода он выполняет связанный блок CATCH. В блоке CATCH мы будем выводить подробную информацию о возникшей ошибке.
BEGIN try
RAISERROR ('Error invoked in the TRY code block.',16,1 );
END try
BEGIN catch
DECLARE @ErrorMsg NVARCHAR(4000);
DECLARE @ErrSeverity INT;
DECLARE @ErrState INT;
SELECT @ErrorMsg = Error_message(),
@ErrSeverity = Error_severity(),
@ErrState = Error_state();
RAISERROR (@ErrorMsg,
@ErrSeverity,
@ErrState
);
END catch;
Так мы добавили оператор RAISERROR с ВАЖНОСТЬЮ МЕЖДУ 11 И 19. Это вызывает выполнение блока CATCH.
В блоке CATCH мы показываем информацию об исходной ошибке, используя оператор RAISERROR.
Вывод:
Как вы можете увидеть, код вернул информацию об исходной ошибке.
Теперь давайте разберемся, как добавить пользовательское сообщение, используя хранимую процедуру sp_addmessage.
Хранимая процедура sp_addmessage
Мы можем добавить пользовательское сообщение, выполнив хранимую процедуру sp_addmessages. Синтаксис процедуры:
EXEC Sp_addmessage
@msgnum= 70001,
@severity=16,
@msgtext='Please enter the numeric value',
@lang=NULL,
@with_log='TRUE',
@replace='Replace';
@msgnum: задает номер сообщения. Тип данных параметра — integer. Это ID пользовательского сообщения.
@severity: указывает уровень серьезности ошибки. Допустимые значения от 1 до 25. Тип данных параметра — smallint.
@messagetext: задает текст сообщения, который вы хотите выводить. Тип данных параметра nvarchar(255), значение по умолчанию NULL.
@lang: задает язык, который вы хотите использовать для вывода сообщения об ошибке. Значение по умолчанию NULL.
@with_log: этот параметр используется для записи сообщения в просмотрщик событий. Допустимые значения TRUE и FALSE. Если вы задаете TRUE, сообщение об ошибке будет записано в просмотрщик событий Windows. Если выбрать FALSE, ошибка не будет записана в журнал ошибок Windows.
@replace: если вы хотите заменить существующее сообщение об ошибке на пользовательское сообщение и уровень серьезности, вы можете указать это в хранимой процедуре.
Предположим, что вы хотите создать сообщение об ошибке, которое возвращает ошибку о недопустимом качестве (invalid quality). В операторе INSERT значение invalid_quality находится в диапазоне между 20 и 100. Сообщение следует рассматривать как ошибку с уровнем серьезности 16.
Чтобы создать такое сообщение, выполните следующий запрос:
USE master;
go
EXEC Sp_addmessage
70001,
16,
N'Product Quantity must be between 20 and 100.';
go
После добавления сообщения выполните запрос ниже, чтобы увидеть его:
USE master
go
SELECT * FROM sys.messages WHERE message_id = 70001
Вывод:
Как использовать пользовательские сообщения об ошибках
Как упоминалось выше, мы должны использовать message_id в операторе RAISERROR для пользовательских сообщений.
Мы создали сообщение с ID = 70001. Оператор RAISERROR должен быть таким:
USE master
go
RAISERROR (70001,16,1 );
go
Вывод:
Оператор RAISERROR вернул пользовательское сообщение.
Хранимая процедура sp_dropmessage
Хранимая процедура sp_dropmessage используется для удаления пользовательских сообщений. Синтаксис оператора:
EXEC Sp_dropmessage @msgnum
Здесь @msgnum задает ID сообщения, которое вы хотите удалить.
Теперь мы хотим удалить сообщение, с ID = 70001. Запрос:
EXEC Sp_dropmessage 70001
Выполним следующий запрос для просмотра сообщения после его удаления:
USE master
go
SELECT * FROM sys.messages WHERE message_id = 70001
Вывод:
Как видно, сообщение было удалено.
Обработка исключений хранимых процедур MySQL
При использовании хранимых процедур MySQL мы часто сталкиваемся с некоторыми проблемами, в результате которых фактические результаты не достигаются в соответствии с логикой кода.В настоящее время необходимо фиксировать и распечатывать ошибки оператора SQL в хранимой процедуре.
Нужно знать концепции
- condition
- hanlder
- Область диагностики
- Определить условие
DECLARE condition_name CONDITION FOR condition_value
condition_value:
mysql_error_code
| SQLSTATE [VALUE] sqlstate_value
Функция этого оператора состоит в том, чтобы связать ошибку, которую необходимо обработать, с именем, которое будет вызываться в следующем операторе объявления-обработчика. Фактически, он должен дать имя обрабатываемой ошибке, чтобы ее можно было вызвать позже.
Например: определить условие «таблица не содержит ошибок»
DECLARE no_such_table CONDITION FOR 1051;
DECLARE CONTINUE HANDLER FOR no_such_table
BEGIN
-- body of handler
END;
DECLARE no_such_table CONDITION FOR SQLSTATE '42S02';
DECLARE CONTINUE HANDLER FOR no_such_table
BEGIN
-- body of handler
END;
2.declare handler
DECLARE handler_action HANDLER
FOR condition_value [, condition_value] ...
statement
handler_action:
CONTINUE
| EXIT
| UNDO
condition_value:
mysql_error_code
| SQLSTATE [VALUE] sqlstate_value
| condition_name
| SQLWARNING
| NOT FOUND
| SQLEXCEPTION
Обработчик используется для обработки одного или нескольких условий.Если условие в операторе определения обработчика возникает, то выполняется оператор, определенный в обработчике.
Оператор обработчика объявления должен появляться после оператора условия объявления и оператора определения переменной.
Пример:
1. Используйте код ошибки для определения обработчика.
DECLARE CONTINUE HANDLER FOR 1051
BEGIN
-- body of handler
END;
2. Используйте значение sqlstate для определения обработчика.
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
BEGIN
-- body of handler
END;
3.SQLWARNING представляет значение sqlstate, начинающееся с 01
DECLARE CONTINUE HANDLER FOR SQLWARNING
BEGIN
-- body of handler
END;
4.NOT FOUND представляет значение sqlstate, начинающееся с 02
DECLARE CONTINUE HANDLER FOR NOT FOUND
BEGIN
-- body of handler
END;
5.SQLEXCEPTION представляет значение SQLstate, начинающееся с 00, 01, 02.
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
-- body of handler
END;
Одно замечание:
В хранимой процедуре, если условие определено, но обработчик не определен, в это время, если возникает ошибка, определенная в условии, MySQL обработает ее следующим образом, и метод обработки зависит от условия Виды.
Условие типа SQLEXCEPTION
Обработчик EXIT по умолчанию используется для обработки. Если хранимая процедура вызывается другой хранимой процедурой в это время, для обработки будет использоваться обработчик, определенный в вызывающей программе.
SQLWARNING условие типа
По умолчанию для обработки используется обработчик CONTINUE, и хранимая процедура продолжает выполняться.
Условие типа НЕ НАЙДЕНО
Если условие выполняется нормально, хранимая процедура выполняется нормально, если оно вызывается SIGNAL или RESIGNAL, хранимая процедура завершается.
нота:
Оператор объявления условия должен появиться перед объявлением курсора и объявлением обработчика
Взгляните на пример официального сайта:
mysql> CREATE TABLE test.t (s1 INT, PRIMARY KEY (s1));
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter //
mysql> CREATE PROCEDURE handlerdemo ()
-> BEGIN
-> DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
-> SET @x = 1;
-> INSERT INTO test.t VALUES (1);
-> SET @x = 2;
-> INSERT INTO test.t VALUES (1);
-> SET @x = 3;
-> END;
-> //
Query OK, 0 rows affected (0.00 sec)
mysql> CALL handlerdemo()//
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @x//
+------+
| @x |
+------+
| 3 |
+------+
1 row in set (0.00 sec)
Если вы хотите, чтобы обработчик не выполнял никакой обработки захваченного условия, вы можете определить обработчик следующим образом:
DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END;
Примечание два
Диапазон кода тега не включает диапазон кода обработчика объявления.
В этом примере область повторения тега — это область цикла повторения.
означает, что повторная попытка тега не может быть вызвана в обработчике
CREATE PROCEDURE p ()
BEGIN
DECLARE i INT DEFAULT 3;
retry:
REPEAT
BEGIN
DECLARE CONTINUE HANDLER FOR SQLWARNING
BEGIN
ITERATE retry; # Это запрещено
END;
IF i < 0 THEN
LEAVE retry; # Необходимо вызвать повторную попытку метки вне определения объявления
END IF;
SET i = i - 1;
END;
UNTIL FALSE END REPEAT;
END;
3. Область диагностики
Выполнение оператора SQL генерирует диагностическую информацию и сохраняет ее в области диагностики.
Используйте оператор GET DIAGNOSTICS, чтобы получить содержимое в области диагностики, которое не требует специальных разрешений.
Диагностика делится на текущую область диагностики и область диагностики стека. Содержимое текущей области диагностики получается с помощью ключевого слова CURRENT, а содержимое области диагностики стека получается с помощью STACKED. Область диагностики стека может использоваться только в том случае, если контекст является обработчиком состояния. Если ключевое слово не указано, информация по умолчанию будет получена из текущей области диагностики.
Получить данные в области диагностики на клиенте
DROP TABLE test.no_such_table;
GET DIAGNOSTICS CONDITION 1
@p1 = RETURNED_SQLSTATE, @p2 = MESSAGE_TEXT;
SELECT @p1, @p2;
В настоящее время нельзя использовать содержимое области диагностики стека GET STACKED DIAGNOSTICS.
, потому что GET STACKED DIAGNOSTICS можно использовать только в обработчике условий.
Сводка области диагностики содержит информацию двух типов:
1. Информация об утверждении, например количество условий и количество затронутых строк.
2. Информация об условиях, включая коды ошибок и сообщения об ошибках. Если оператор SQL выдает несколько условий, то в этой части области диагностики область условий будет выделена для каждого условия. Если Если условие не выбрано, оно не будет назначено
В стандарте SQL первое условие касается значения SQLstate, возвращаемого предыдущим оператором SQL.
Но в MySQL это не может быть гарантировано. Для получения основной ошибки нельзя использовать следующий метод:
GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;
Вместо этого сначала получите номер условия, а затем используйте значение, чтобы указать условие для просмотра.
Правильный способ:
GET DIAGNOSTICS @cno = NUMBER;
GET DIAGNOSTICS CONDITION @cno @errno = MYSQL_ERRNO;
Что касается области диагностики, примеры официального сайта:
CREATE PROCEDURE do_insert(value INT)
BEGIN
-- Declare variables to hold diagnostics area information
DECLARE code CHAR(5) DEFAULT '00000';
DECLARE msg TEXT;
DECLARE rows INT;
DECLARE result TEXT;
-- Declare exception handler for failed insert
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
code = RETURNED_SQLSTATE, msg = MESSAGE_TEXT;
END;
-- Perform the insert
INSERT INTO t1 (int_col) VALUES(value);
-- Check whether the insert was successful
IF code = '00000' THEN
GET DIAGNOSTICS rows = ROW_COUNT;
SET result = CONCAT('insert succeeded, row count = ',rows);
ELSE
SET result = CONCAT('insert failed, error = ',code,', message = ',msg);
END IF;
-- Say what happened
SELECT result;
END;
Предполагая, что тип поля таблицы t1 в приведенной выше хранимой процедуре — int, а не null, выполните следующие операции для вставки ненулевых и нулевых значений в таблицу t1, соответственно, и получите следующие результаты:
# Вставить ненулевое значение
mysql> CALL do_insert(1);
+---------------------------------+
| result |
+---------------------------------+
| insert succeeded, row count = 1 |
+---------------------------------+
## Insert null
mysql> CALL do_insert(NULL);
+---- ------------------------------------------------------------+
| result |
+-----------------------------------------------------------------+insert failed, error = 23000, message = Column 'int_col' cannot be null
+-----------------------------------------------------------------+
Когда обработчик условий в хранимой процедуре активируется, произойдет событие, помещенное в стек в области диагностики:
1. Текущая область диагностики (первая область диагностики) станет областью диагностики стека (вторая область диагностики), а новая область диагностики будет создана как область текущей диагностики.
2. В обработчике условий GET [CURRENT] DIAGNOSTICS и GET STACKED DIAGNOSTICS могут использоваться для получения содержимого текущей области диагностики или области диагностики стека.
3. Вначале текущая область диагностики и область диагностики стека будут возвращать один и тот же результат, поэтому можно получить соответствующую информацию об активированном состоянии обработчика из текущей области диагностики, если В настоящее время в обработчике нет другого оператора SQL для изменения содержимого в текущей области диагностики.
4. При выполнении оператора в обработчике содержимое текущей области диагностики будет очищено или изменено в соответствии с определенными правилами.
Таким образом, более надежный способ получить информацию в активированном обработчике условий — получить соответствующую информацию из области диагностики стека, потому что содержимое области диагностики стека не будет изменяться операторами в обработчике условий, за исключением операторов RESIGNAL.
В следующем примере показано, как получить информацию об исключениях обработчика с помощью оператора GET STACKED DIAGNOSTICS в условии, даже если текущая область диагностики была очищена или изменена в это время.
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (c1 TEXT NOT NULL);
DROP PROCEDURE IF EXISTS p;
delimiter //
CREATE PROCEDURE p ()
BEGIN
-- Declare variables to hold diagnostics area information
DECLARE errcount INT;
DECLARE errno INT;
DECLARE msg TEXT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- Here the current DA is nonempty because no prior statements
-- executing within the handler have cleared it
GET CURRENT DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'current DA before mapped insert' AS op, errno, msg;
GET STACKED DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'stacked DA before mapped insert' AS op, errno, msg;
-- Map attempted NULL insert to empty string insert
INSERT INTO t1 (c1) VALUES('');
-- Here the current DA should be empty (if the INSERT succeeded),
-- so check whether there are conditions before attempting to
-- obtain condition information
GET CURRENT DIAGNOSTICS errcount = NUMBER;
IF errcount = 0
THEN
SELECT 'mapped insert succeeded, current DA is empty' AS op;
ELSE
GET CURRENT DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'current DA after mapped insert' AS op, errno, msg;
END IF ;
GET STACKED DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'stacked DA after mapped insert' AS op, errno, msg;
END;
INSERT INTO t1 (c1) VALUES('string 1');
INSERT INTO t1 (c1) VALUES(NULL);
END;
//
delimiter ;
CALL p();
SELECT * FROM t1;
В вышеупомянутой хранимой процедуре определяется обработчик состояния. В начале этого обработчика получается содержимое текущей области диагностики и области диагностики стека, затем выполняется инструкция вставки, а затем запрашивается содержимое текущей области диагностики и области диагностики стека соответственно. . После завершения определения обработчика это основное тело хранимой процедуры, то есть два оператора вставки. Один из операторов вставки является ненулевой строкой, а другой вставкой — нулевым значением, поэтому последовательность выполнения хранимой процедуры выглядит следующим образом:
1. Сначала успешно выполните INSERT INTO t1 (c1) VALUES («строка 1»);
2. Выполните INSERT INTO t1 (c1) VALUES (NULL); поскольку вставлять нулевые значения в таблицу t1 запрещено, будет выдано исключение.
3. Сгенерированное исключение перехватывается обработчиком условия, и обработчик условия активируется для запуска логики обработки в нем и печати содержимого текущей области диагностики и области диагностики стека в обработчике условий, оба содержимого тот же самый.
4. Выполняется инструкция INSERT INTO t1 (c1) VALUES (’’); в обработчике условий, и выполнение инструкции очищает содержимое в текущей области диагностики.
±———————————±——±—————————+
| op | errno | msg |
±———————————±——±—————————+
| stacked DA before mapped insert | 1048 | Column ‘c1’ cannot be null |
±———————————±——±—————————+
5. Снова распечатайте содержимое области текущей диагностики и области диагностики стека в обработчике состояния. В это время, поскольку содержимое текущей области диагностики опустошено, напечатайте «сопоставленная вставка выполнена успешно, текущий DA пуст», а затем распечатайте содержимое области диагностики стека, потому что Содержимое в области диагностики стека не будет очищено при выполнении оператора, поэтому содержимое, отображаемое в области диагностики стека, остается:
±——————————-±——±—————————+
| op | errno | msg |
±——————————-±——±—————————+
| stacked DA after mapped insert | 1048 | Column ‘c1’ cannot be null |
±——————————-±——±—————————+
нужно знать
1. Оператор GET DIAGNOSTICS также очищает содержимое в текущей области диагностики, поэтому удалите оператор вставки в обработчике условия в приведенном выше коде, и результат будет таким же.
2. Если указанная выше хранимая процедура изменяется следующим образом, то есть оператор трех объявляемых переменных помещается в
В обработчике объявления фактический результат будет зависеть от версии MySQL. Если это версия до MySQL-5.7.2, следующие изменения не повлияют на содержимое области диагностики. Фактический результат такой же, как и результат выше.Если он находится в MySQL-5.7.2 и более поздних версиях, оператор объявления переменной очистит содержимое в текущей области диагностики.
CREATE PROCEDURE p ()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- Declare variables to hold diagnostics area information
DECLARE errcount INT;
DECLARE errno INT;
DECLARE msg TEXT;
GET CURRENT DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'current DA before mapped insert' AS op, errno, msg;
GET STACKED DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'stacked DA before mapped insert' AS op, errno, msg;
...
Поэтому, когда вам нужно получить содержимое в области диагностики, вы должны получить его из области диагностики стека, а не из текущей области диагностики.
В этой статье мы расскажем вам, как использовать обработчик MySQL для обработки исключений или ошибок, возникающих в хранимых процедурах.
При возникновении ошибки внутри хранимой процедуры, важно исправить ее соответствующим образом, например, продолжая или останавливая исполнение операции и выдавая сообщение об ошибке.
MySQL предоставляет простой способ определить обработчики, которые обрабатывают ошибки, исходя из общих условий, таких как предупреждения или исключения из условий, например, конкретные коды ошибок.
- Объявление обработчика
- Примеры обработки ошибок MySQL
- Пример обработчика MySQL в хранимых процедурах
- Приоритет обработчиков MySQL
- Использование проименованных условий ошибки
Чтобы объявить обработчик мы используем оператор DECLARE HANDLER:
DECLARE action HANDLER FOR condition_value statement;
Если значение условия совпадает со значением condition_value, MySQL выполнит оператор statement и продолжит или завершит текущий блок кода, исходя из значения action.
action может принимать следующие значения:
- CONTINUE: исполнение блокированного кода (BEGIN … END) продолжается;
- EXIT: выполнение блокированного кода, в котором был объявлен обработчик, завершается.
condition_value задает конкретное условие или класс условия, которые активируют обработчик.
condition_value может принимать одно из следующих значений:
- код ошибки MySQL;
- стандартное значение SQLSTATE. Или это может быть условие SQLWARNING, NOTFOUND или SQLEXCEPTION, которое является сокращением для класса значений SQLSTATE. Условие NOTFOUND используется для курсора или оператора SELECT INTO variable_list;
- название условия, связанного либо с кодом ошибки MySQL, либо со значением SQLSTATE.
В качестве statement может использоваться простой оператор или составной оператор, вшитый с помощью ключевых слов BEGIN и END.
Давайте рассмотрим несколько примеров объявления обработчиков.
Обработчик, приведенный ниже, означает: когда происходит ошибка, устанавливается значение переменной has_error 1 и выполнение продолжается:
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET has_error = 1;
Ниже приводится другой обработчик, который означает, что в случае возникновении любой ошибки, производится откат предыдущей операции, выдается сообщение об ошибке и осуществляется выход из текущего блока кода.
Если вы объявляете его внутри блока BEGIN END хранимой процедуры, он немедленно завершает хранимую процедуру:
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; SELECT 'An error has occurred, operation rollbacked and the stored procedure was terminated'; END;
Если строк для вывода больше нет, для вариантов cursor или оператора SELECT INTO, значение переменной no_row_found устанавливается равным 1 и продолжается исполнение:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_row_found = 1;
При возникновении ошибки дублирования ключа, выдается ошибка MySQL 1062. Следующий обработчик выдает сообщение об ошибке и продолжает выполнение:
DECLARE CONTINUE HANDLER FOR 1062 SELECT 'Error, duplicate key occurred';
Во-первых, для демонстрации мы создаем новую таблицу с именем article_tags:
CREATE TABLE article_tags( article_id INT, tag_id INT, PRIMARY KEY(article_id,tag_id) );
В таблице article_tags хранятся связи между статьями и тегами. К каждой статье может относиться несколько тегов и наоборот.
Для простоты, мы не будем создавать таблицы articles и tags, а также внешние ключи в таблице article_tags.
Во-вторых, мы создаем хранимую процедуру, которая вставляет пару идентификаторов статьи и тега в таблицу article_tags:
DELIMITER $$ CREATE PROCEDURE insert_article_tags(IN article_id INT, IN tag_id INT) BEGIN DECLARE CONTINUE HANDLER FOR 1062 SELECT CONCAT('duplicate keys (',article_id,',',tag_id,') found') AS msg; -- insert a new record into article_tags INSERT INTO article_tags(article_id,tag_id) VALUES(article_id,tag_id); -- return tag count for the article SELECT COUNT(*) FROM article_tags; END
В-третьих, для статьи 1 мы добавляем идентификаторы тега 1, 2 и 3, с помощью вызова хранимой процедуры insert_article_tags:
CALL insert_article_tags(1,1); CALL insert_article_tags(1,2); CALL insert_article_tags(1,3);
Четвертое. Давайте попробуем вставить дубликат ключа, чтобы увидеть, действительно ли вызывается обработчик:
CALL insert_article_tags(1,3);
Мы получили сообщение об ошибке. Однако, поскольку мы объявили тип обработчика CONTINUE, хранимая процедура продолжает исполняться.
В результате, мы все равно получили список тегов для статьи:
Если мы в объявлении обработчика изменим команду CONTINUE на EXIT, мы получим только сообщение об ошибке:
DELIMITER $$ CREATE PROCEDURE insert_article_tags_2(IN article_id INT, IN tag_id INT) BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT 'SQLException invoked'; DECLARE EXIT HANDLER FOR 1062 SELECT 'MySQL error code 1062 invoked'; DECLARE EXIT HANDLER FOR SQLSTATE '23000' SELECT 'SQLSTATE 23000 invoked'; -- insert a new record into article_tags INSERT INTO article_tags(article_id,tag_id) VALUES(article_id,tag_id); -- return tag count for the article SELECT COUNT(*) FROM article_tags; END
Теперь, мы можем попробовать добавить дубликат ключа, чтобы увидеть результат:
CALL insert_article_tags_2(1,3);
В случае если у вас есть несколько обработчиков, которые имеют право обрабатывать ошибку, MySQL для обработки ошибки будет вызывать наиболее подходящий обработчик.
Ошибка всегда обозначается одним из кодов ошибки MySQL, так что MySQL в этом плане имеет возможность четко их идентифицировать.
Обозначения SQLSTATE для многих кодов ошибок MySQL менее специфичны. SQLEXCPETION или SQLWARNING представляют собой сокращения класса значений SQLSTATES, поэтому они имеют общий характер.
На основании правил приоритета обработчиков обработчик кода ошибки MySQL, обработчик SQLSTATE и обработчик SQLEXCEPTION имеют приоритеты один, два и три соответственно.
Предположим, что в хранимой процедуре insert_article_tags_3 мы объявляем три обработчика:
DELIMITER $$ CREATE PROCEDURE insert_article_tags_3(IN article_id INT, IN tag_id INT) BEGIN DECLARE EXIT HANDLER FOR 1062 SELECT 'Duplicate keys error encountered'; DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT 'SQLException encountered'; DECLARE EXIT HANDLER FOR SQLSTATE '23000' SELECT 'SQLSTATE 23000'; -- insert a new record into article_tags INSERT INTO article_tags(article_id,tag_id) VALUES(article_id,tag_id); -- return tag count for the article SELECT COUNT(*) FROM article_tags; END
Теперь мы пробуем добавить в таблицу article_tags дубликат ключа через вызов хранимой процедуры:
CALL insert_article_tags_3(1,3);
Как видите, вызывается обработчик кода ошибки MySQL:
Начинаем с объявления обработчика ошибки:
DECLARE EXIT HANDLER FOR 1051 SELECT 'Please create table abc first'; SELECT * FROM abc;
Что означает код 1051? Представьте, что у вас есть большая хранимая процедура, по всему коду которой разбросаны некорректные значения. Настоящий кошмар для разработчиков обслуживания.
К счастью, MySQL предоставляет нам оператор DECLARE CONDITION, который объявляет проименованное условие ошибки, связанное с условием.
Синтаксис оператора DECLARE CONDITION выглядит следующим образом:
DECLARE condition_name CONDITION FOR condition_value;
condition_value может представлять собой код ошибки MySQL, например 1015, или значение SQLSTATE. condition_value представляется с помощью condition_name.
После объявления вы можете обращаться к condition_name вместо condition_value.
Таким образом, мы можем переписать код, приведенный выше, следующим образом:
DECLARE table_not_found CONDITION for 1051; DECLARE EXIT HANDLER FOR table_not_found SELECT 'Please create table abc first'; SELECT * FROM abc;
Этот код, очевидно, более удобен для чтения, нежели предыдущий. Отметим, что объявление условия должно размещаться перед объявлением обработчика или объявлением курсора.
This article is half-done without your Comment! *** Please share your thoughts via Comment ***
In this post, I am sharing the full demonstration on how to manage error/exception handling in the Stored Procedure of MySQL.
Whenever an exception is occurring in a stored procedure, it is very important to handle it by showing proper error messages.
If you do not handle the exception, there would be a chance to fail application with the certain exception in a stored procedure.
If you get an error in stored procedure, instead of an exit, you should continue without any error message. That means you can show any default or custom error code or message to the application/user.
MySQL provides Handler to handle the exception in the stored procedure.
Below is a full demonstration of a handler with examples:
/*Create Employee database for demo */ CREATE DATABASE Employee; /*Create sample EmployeeDetails table.*/ CREATE TABLE Employee.tbl_EmployeeDetails ( EmpID INTEGER ,EmpName VARCHAR(50) ,EmailAddress VARCHAR(50) ,CONSTRAINT pk_tbl_EmployeeDetails_EmpID PRIMARY KEY (EmpID) )ENGINE = InnoDB; |
How to declare handler in store procedure:
Syntax of Handler:
DECLARE handler_action HANDLER FOR condition_value … statement |
Three type of Handler_Action:
- CONTINUE
- EXIT
- UNDO
Type of Condition Value:
- mysql_error_code
- sqlstate_value
- SQLWarning
- SQLException
- NotFound
How to write handler in stored procedure?
E.g.
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SELECT ‘Error occured’; DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET IsError=1; DECLARE EXIT HANDLER FOR SQLEXCEPTION SET IsError=1; DECLARE EXIT HANDLER FOR SQLSTATE ‘23000’ SET IsError = 1; |
The Above are four different handler examples. Now, I am going to insert a duplicate value into EmpID column.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
DELIMITER // CREATE PROCEDURE Employee.usp_InsertEmployeeDetails ( InputEmpID INTEGER ,InputEmpName VARCHAR(50) ,InputEmailAddress VARCHAR(50) ) /**************************************************************** Authors Name : Anvesh Patel Created Date : 2015-05-20 Description : This is demo stored procedure to insert record into table with proper error handling.Basically for www.dbrnd.com readers. *****************************************************************/ BEGIN DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SELECT ‘Error occured’; INSERT INTO Employee.tbl_EmployeeDetails ( EmpID ,EmpName ,EmailAddress ) VALUES ( InputEmpID ,InputEmpName ,InputEmailAddress ); SELECT *FROM Employee.tbl_EmployeeDetails; END // DELIMITER ; |
In the above SP, I defined a CONTINUE handler with my custom exception message.
Now, call the above SP two times with same EmpID.
The first time, it will execute successfully, but the second time it will throw a custom error message.
As we defined CONTINUE handler, so it will just show an error message and CONTINUE to next part of the SELECT statement.
Let’s See,
CALL Employee.usp_InsertEmployeeDetails (1,’Anvesh’,’anvesh@gmail.com’); CALL Employee.usp_InsertEmployeeDetails (1,’Roy’,’Roy@gmail.com’); |
Above are the two different calls with same EmpID value. The first call executes without any error message and the second call execute with an error message.
The resule of Second Call:
As we defined CONTINUE, so you can find two results in above image. One is our custom error message and second is the result of the defined SELECT statement.
The execution didn’t stop by error, and it continued for another part.
Now, check the EXIT handler:
Please modify your handler and replace CONTINUE by EXIT:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
DELIMITER // CREATE PROCEDURE Employee.usp_InsertEmployeeDetails ( InputEmpID INTEGER ,InputEmpName VARCHAR(50) ,InputEmailAddress VARCHAR(50) ) /***************************************************************** Authors Name : Anvesh Patel Created Date : 2015-05-20 Description : This is demo stored procedure to insert record into table with proper error handling.Basically for www.dbrnd.com readers. ******************************************************************/ BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT ‘Error occured’; INSERT INTO Employee.tbl_EmployeeDetails ( EmpID ,EmpName ,EmailAddress ) VALUES ( InputEmpID ,InputEmpName ,InputEmailAddress ); SELECT *FROM Employee.tbl_EmployeeDetails; END // DELIMITER ; |
Call with the same parameter:
CALL Employee.usp_InsertEmployeeDetails (1,’Roy’,’Roy@gmail.com’); |
The Result is an only error message, and you cannot find two results as we defined EXIT to exit the code when an error occurred.
The best practice is to create a output parameter and store 1 if any error occurred.
Application code has to check this output parameter is NULL or 1.
1 = Error.
NULL = No Error.
Below is a stored procedure for this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
DELIMITER // CREATE PROCEDURE Employee.usp_InsertEmployeeDetails ( InputEmpID INTEGER ,InputEmpName VARCHAR(50) ,InputEmailAddress VARCHAR(50) ,out IsError INTEGER ) /*********************************************************** Authors Name : Anvesh Patel Created Date : 2015-05-20 Description : This is demo stored procedure to insert record into table with proper error handling. Basically for www.dbrnd.com readers. ************************************************************/ BEGIN DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET IsError=1; INSERT INTO Employee.tbl_EmployeeDetails ( EmpID ,EmpName ,EmailAddress ) VALUES ( InputEmpID ,InputEmpName ,InputEmailAddress ); SELECT *FROM Employee.tbl_EmployeeDetails; END // DELIMITER ; |
Now call the above SP and select output parameter:
CALL Employee.usp_InsertEmployeeDetails (1,’Roy’,’Roy@gmail.com’,@IsError); SELECT @IsError; |
Now Results are:
Above is a simple demonstration of Error Handling in MySQL. You can also use SQLSTATE which shows default error messages of MySQL.
May 21, 2015