В этой статье мы расскажем вам, как использовать обработчик 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;
Этот код, очевидно, более удобен для чтения, нежели предыдущий. Отметим, что объявление условия должно размещаться перед объявлением обработчика или объявлением курсора.
Summary: in this tutorial, you will learn how to use MySQL handler to handle errors encountered in stored procedures.
When an error occurs inside a stored procedure, it is important to handle it appropriately, such as continuing or exiting the current code block’s execution, and issuing a meaningful error message.
MySQL provides an easy way to define handlers that handle from general conditions such as warnings or exceptions to specific conditions e.g., specific error codes.
Declaring a handler
To declare a handler, you use the DECLARE HANDLER
statement as follows:
DECLARE action HANDLER FOR condition_value statement;
Code language: SQL (Structured Query Language) (sql)
If a condition whose value matches the condition_value
, MySQL will execute the statement
and continue or exit the current code block based on the action
.
The action
accepts one of the following values:
CONTINUE
: the execution of the enclosing code block (BEGIN
…END
) continues.EXIT
: the execution of the enclosing code block, where the handler is declared, terminates.
The condition_value
specifies a particular condition or a class of conditions that activate the handler. The condition_value
accepts one of the following values:
- A MySQL error code.
- A standard
SQLSTATE
value. Or it can be anSQLWARNING
,NOTFOUND
orSQLEXCEPTION
condition, which is shorthand for the class ofSQLSTATE
values. TheNOTFOUND
condition is used for a cursor orSELECT INTO variable_list
statement. - A named condition associated with either a MySQL error code or
SQLSTATE
value.
The statement
could be a simple statement or a compound statement enclosing by the BEGIN
and END
keywords.
MySQL error handling examples
Let’s take some examples of declaring handlers.
The following handler set the value of the hasError
variable to 1 and continue the execution if an SQLEXCEPTION
occurs
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SET hasError = 1;
Code language: SQL (Structured Query Language) (sql)
The following handler rolls back the previous operations, issues an error message, and exit the current code block in case an error occurs. If you declare it inside the BEGIN END
block of a stored procedure, it will terminate the stored procedure immediately.
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT 'An error has occurred, operation rollbacked and the stored procedure was terminated';
END;
Code language: SQL (Structured Query Language) (sql)
The following handler sets the value of the RowNotFound
variable to 1 and continues execution if there is no more row to fetch in case of a cursor or SELECT INTO
statement:
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET RowNotFound = 1;
Code language: SQL (Structured Query Language) (sql)
If a duplicate key error occurs, the following handler issues an error message and continues execution.
DECLARE CONTINUE HANDLER FOR 1062
SELECT 'Error, duplicate key occurred';
Code language: SQL (Structured Query Language) (sql)
MySQL handler example in stored procedures
First, create a new table named SupplierProducts
for the demonstration:
CREATE TABLE SupplierProducts (
supplierId INT,
productId INT,
PRIMARY KEY (supplierId , productId)
);
Code language: SQL (Structured Query Language) (sql)
The table SupplierProducts
stores the relationships between the table suppliers and products. Each supplier may provide many products and each product can be provided by many suppliers. For the sake of simplicity, we don’t create Products
and Suppliers
tables, as well as the foreign keys in the SupplierProducts
table.
Second, create a stored procedure that inserts product id and supplier id into the SupplierProducts
table:
CREATE PROCEDURE InsertSupplierProduct(
IN inSupplierId INT,
IN inProductId INT
)
BEGIN
-- exit if the duplicate key occurs
DECLARE EXIT HANDLER FOR 1062
BEGIN
SELECT CONCAT('Duplicate key (',inSupplierId,',',inProductId,') occurred') AS message;
END;
-- insert a new row into the SupplierProducts
INSERT INTO SupplierProducts(supplierId,productId)
VALUES(inSupplierId,inProductId);
-- return the products supplied by the supplier id
SELECT COUNT(*)
FROM SupplierProducts
WHERE supplierId = inSupplierId;
END$$
DELIMITER ;
Code language: SQL (Structured Query Language) (sql)
How it works.
The following exit handler terminates the stored procedure whenever a duplicate key occurs (with code 1062). In addition, it returns an error message.
DECLARE EXIT HANDLER FOR 1062
BEGIN
SELECT CONCAT('Duplicate key (',supplierId,',',productId,') occurred') AS message;
END;
Code language: SQL (Structured Query Language) (sql)
This statement inserts a row into the SupplierProducts
table. If a duplicate key occurs, the code in the handler section will execute.
INSERT INTO SupplierProducts(supplierId,productId)
VALUES(supplierId,productId);
Code language: SQL (Structured Query Language) (sql)
Third, call the InsertSupplierProduct()
to insert some rows into the SupplierProducts
table:
CALL InsertSupplierProduct(1,1);
CALL InsertSupplierProduct(1,2);
CALL InsertSupplierProduct(1,3);
Code language: SQL (Structured Query Language) (sql)
Fourth, attempt to insert a row whose values already exist in the SupplierProducts
table:
CALL InsertSupplierProduct(1,3);
Code language: SQL (Structured Query Language) (sql)
Here is the error message:
+------------------------------+
| message |
+------------------------------+
| Duplicate key (1,3) occurred |
+------------------------------+
1 row in set (0.01 sec)
Code language: JavaScript (javascript)
Because the handler is an EXIT
handler, the last statement does not execute:
SELECT COUNT(*)
FROM SupplierProducts
WHERE supplierId = inSupplierId;
Code language: SQL (Structured Query Language) (sql)
If you change the EXIT
in the handler declaration to CONTINUE
, you will also get the number of products provided by the supplier:
DROP PROCEDURE IF EXISTS InsertSupplierProduct;
DELIMITER $$
CREATE PROCEDURE InsertSupplierProduct(
IN inSupplierId INT,
IN inProductId INT
)
BEGIN
-- exit if the duplicate key occurs
DECLARE CONTINUE HANDLER FOR 1062
BEGIN
SELECT CONCAT('Duplicate key (',inSupplierId,',',inProductId,') occurred') AS message;
END;
-- insert a new row into the SupplierProducts
INSERT INTO SupplierProducts(supplierId,productId)
VALUES(inSupplierId,inProductId);
-- return the products supplied by the supplier id
SELECT COUNT(*)
FROM SupplierProducts
WHERE supplierId = inSupplierId;
END$$
DELIMITER ;
Code language: SQL (Structured Query Language) (sql)
Finally, call the stored procedure again to see the effect of the CONTINUE
handler:
CALL InsertSupplierProduct(1,3);
Code language: SQL (Structured Query Language) (sql)
Here is the output:
+----------+
| COUNT(*) |
+----------+
| 3 |
+----------+
1 row in set (0.01 sec)
Query OK, 0 rows affected (0.02 sec)
Code language: SQL (Structured Query Language) (sql)
MySQL handler precedence
In case you have multiple handlers that handle the same error, MySQL will call the most specific handler to handle the error first based on the following rules:
- An error always maps to a MySQL error code because in MySQL it is the most specific.
- An
SQLSTATE
may map to many MySQL error codes, therefore, it is less specific. - An
SQLEXCPETION
or anSQLWARNING
is the shorthand for a class ofSQLSTATES
values so it is the most generic.
Based on the handler precedence rules, MySQL error code handler, SQLSTATE
handler and SQLEXCEPTION
takes the first, second and third precedence.
Suppose that we have three handlers in the handlers in the stored procedure insert_article_tags_3
:
DROP PROCEDURE IF EXISTS InsertSupplierProduct;
DELIMITER $$
CREATE PROCEDURE InsertSupplierProduct(
IN inSupplierId INT,
IN inProductId INT
)
BEGIN
-- exit if the duplicate key occurs
DECLARE EXIT HANDLER FOR 1062 SELECT 'Duplicate keys error encountered' Message;
DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT 'SQLException encountered' Message;
DECLARE EXIT HANDLER FOR SQLSTATE '23000' SELECT 'SQLSTATE 23000' ErrorCode;
-- insert a new row into the SupplierProducts
INSERT INTO SupplierProducts(supplierId,productId)
VALUES(inSupplierId,inProductId);
-- return the products supplied by the supplier id
SELECT COUNT(*)
FROM SupplierProducts
WHERE supplierId = inSupplierId;
END$$
DELIMITER ;
Code language: SQL (Structured Query Language) (sql)
Call the stored procedure to insert a duplicate key:
CALL InsertSupplierProduct(1,3);
Code language: SQL (Structured Query Language) (sql)
Here is the output:
+----------------------------------+
| Message |
+----------------------------------+
| Duplicate keys error encountered |
+----------------------------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)
Code language: JavaScript (javascript)
As you see the MySQL error code handler is called.
Using a named error condition
Let’s start with an error handler declaration.
DELIMITER $$
CREATE PROCEDURE TestProc()
BEGIN
DECLARE EXIT HANDLER FOR 1146
SELECT 'Please create table abc first' Message;
SELECT * FROM abc;
END$$
DELIMITER ;
Code language: SQL (Structured Query Language) (sql)
What does the number 1146
really mean? Imagine you have stored procedures polluted with these numbers all over places; it will be difficult to understand and maintain the code.
Fortunately, MySQL provides you with the DECLARE CONDITION
statement that declares a named error condition, which associates with a condition.
Here is the syntax of the DECLARE CONDITION
statement:
DECLARE condition_name CONDITION FOR condition_value;
Code language: SQL (Structured Query Language) (sql)
The condition_value
can be a MySQL error code such as 1146
or a SQLSTATE
value. The condition_value
is represented by the condition_name
.
After the declaration, you can refer to condition_name
instead of condition_value
.
So you can rewrite the code above as follows:
DROP PROCEDURE IF EXISTS TestProc;
DELIMITER $$
CREATE PROCEDURE TestProc()
BEGIN
DECLARE TableNotFound CONDITION for 1146 ;
DECLARE EXIT HANDLER FOR TableNotFound
SELECT 'Please create table abc first' Message;
SELECT * FROM abc;
END$$
DELIMITER ;
Code language: SQL (Structured Query Language) (sql)
As you can see, the code is more obviously and readable than the previous one. Notice that the condition declaration must appear before handler or cursor declarations.
In this tutorial, you have learned how to use MySQL handlers to handle exception or errors occurred in stored procedures.
Was this tutorial helpful?
It is very important to handle the errors by throwing a proper error message. MySQL provides a handler to handle the error. We can declare a handler with the help of the following syntax −
Syntax of handler
DECLARE handler_action FOR condition_value statement;
The above syntax shows that we need to use DECLARE HANDLER statement to declare a handler. If a condition whose value matches the condition_value then MySQL will execute the statement and continue or exit the current code block based on the action. Followings are the three major things in the above syntax −
-
Handler_action is of two types and can accept one of the following values −
- CONTINUE − If the handler_action is ‘CONTINUE’ then the execution of the enclosing code block continues.
- EXIT − If the handler_action is ‘EXIT’ then the execution of the enclosing code block terminates.
-
Condition_value specifies a particular condition or class of conditions that activate the handler. It can accept one of the following values −
- ERROR CODE − The condition_value can be a MySQL error code.
- SQLSTATE − The condition_value can be an SQLSTATE also.
- SQLWARNING − The condition_value can be an SQLWARNING also.
- NOTFOUND − The condition_value can be a NOTFOUND also.
- SQLEXCEPTION − The condition_value can be an SQLEXCEPTION also.
-
The Statement can be a simple statement or compound statement enclosing by the BEGIN and END keywords.
Example
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET got_error = 1;
In the above example, a handler means that if an error occurs then set the value of the got_error variable to 10 and continues the execution.
- Related Articles
- How can we write MySQL handler in a stored procedure?
- Why is it necessary to declare NOT FOUND handler while using MySQL cursor?\n
- How can we offload the time/date handling in MySQL?
- What is MySQL NOT NULL constraint and how can we declare a field NOT NULL while creating a table?
- Handling Errors in TypeScript
- Database Handling Errors in Python
- Can we use {} while creating a MySQL table?
- How can we write MySQL handler, in a stored procedure, that use SQLSTATE for default MySQL error and exit the execution?
- How can we use logical operators while creating MySQL views?
- Can we skip a column name while inserting values in MySQL?
- How can we write MySQL handler, in a stored procedure, that throws an error message and continues the execution?
- How can we write MySQL handler, in a stored procedure, that throws an error message and exit the execution?
- Handling errors in SAP GUI Scripting code
- How can we use a combination of logical operators while creating MySQL views?
- Can we declare a constructor as private in Java?
Kickstart Your Career
Get certified by completing the course
Get Started
В MySQL существует несколько способов использования обработчиков ошибок:
1. Конструкция TRY-CATCH: позволяет обработать исключение, возникшее внутри блока TRY. Если исключение возникает, управление передается блоку CATCH, где можно обработать ошибку.
Пример:
BEGIN DECLARE divide_by_zero CONDITION FOR SQLSTATE '22012'; DECLARE EXIT HANDLER FOR divide_by_zero BEGIN SELECT 'Division by zero error'; END; -- код, который может вызвать ошибку деления на ноль END;
2. Конструкция SIGNAL: позволяет сгенерировать исключение с заданным кодом ошибки и сообщением. Сигнал может быть использован для обработки любых типов ошибок, включая пользовательские ошибки.
Пример:
BEGIN DECLARE divide_by_zero CONDITION FOR SQLSTATE '22012'; DECLARE EXIT HANDLER FOR divide_by_zero BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Division by zero error'; END; -- код, который может вызвать ошибку деления на ноль END;
3. Конструкция DECLARE…HANDLER: позволяет определить обработчик для определенного типа ошибки. Здесь можно использовать как встроенные коды ошибок, так и пользовательские.
Пример:
BEGIN DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' BEGIN SELECT 'Duplicate key error'; END; -- код, который может вызвать ошибку дублирования ключа END;
Обработчики ошибок позволяют более гибко управлять логикой выполнения запросов и обеспечивать безопасность базы данных.
В этой статье мы расскажем вам, как использовать обработчик 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;
Этот код, очевидно, более удобен для чтения, нежели предыдущий. Отметим, что объявление условия должно размещаться перед объявлением обработчика или объявлением курсора.
Правильная обработка ошибок позволяет сделать стабильное приложение, которое не будет завершаться неожиданно. В случае возникновения непредвиденной ситуации программа должна выдавать вменяемый ответ, почему она не хочет исполнится. И создание правильного механизма обработки ошибок — это задача программиста.
В этой статье рассмотрим два вида ошибок, которые могут возникнуть при программировании взаимодействия с базой данных. Первый тип — это ошибка подключения к базе данных. Второй тип — это ошибка выполнения запроса к базе данных. Для обработки этих ошибок будем использовать специальные функции для работы с базой.
Ошибка соединения с базой данных
Сразу приведём пример обработки ошибки с соединением с базой данных:
<?php
$host = 'localhost'; // адрес сервера
$db_name = 'database'; // имя базы данных
$user = 'user'; // имя пользователя
$password = 'password'; // пароль
// создание подключения к базе
$connection = mysqli_connect($host, $user, $password, $db_name);
// проверка правильности подключения
if(!$connection){ // при соединении с базой данных возникла ошибка
echo 'Ошибка соединения: ' . mysqli_connect_error() . '<br>';
echo 'Код ошибки: ' . mysqli_connect_errno();
}else{ // соединение было установлено успешно
// здесь можно делать запрос к базе,
// потому что соединение успешно установлено
}
?>
В этом примере можно заметить функцию mysqli_connect_error. Она выводит текстовое описание ошибки подключения (на английском языке). В отличии от неё функция mysqli_connect_errno выводит числовой код ошибки, к примеру «1045».
Ошибка запроса к базе
Теперь попробуем доработать пример из предыдущего параграфа и добавить запрос к базе данных. Не будем вдаваться в детали, что будет записано в тексте SQL запроса, просто предположим, что он может завершиться ошибкой. К примеру, из-за неожиданного отключения сервера с базой данных от сети. В примере добавим проверку на наличие ошибок при выполнении запроса:
<?php
$host = 'localhost'; // адрес сервера
$db_name = 'database'; // имя базы данных
$user = 'user'; // имя пользователя
$password = 'password'; // пароль
// создание подключения к базе
$connection = mysqli_connect($host, $user, $password, $db_name);
if(!$connection){ // проверка правильности подключения
echo 'Ошибка соединения: ' . mysqli_connect_error() . '<br>';
echo 'Код ошибки: ' . mysqli_connect_errno();
}else{ // подключение успешно установлено
// текст SQL запроса, который будет передан базе
$query = 'SELECT * FROM `USERS`';
// выполняем запрос к базе данных
$result = mysqli_query($connection, $query);
if(!$result){ // запрос завершился ошибкой
echo 'Ошибка запроса: ' . mysqli_error($connection) . '<br>';
echo 'Код ошибки: ' . mysqli_errno($connection);
}else{ // запрос успешно выполнился
while($row = $result->fetch_assoc()){
// обрабатываем полученные данные
}
}
// закрываем соединение с базой
mysqli_close($connection);
}
?>
В этом примере есть две функции, которые работают с ошибками базы. Функция mysqli_error возвращает описание ошибки запроса (на английском языке), а функция mysqli_errno возвращает числовой код ошибки, к примеру, «1193».
Обратите внимание, что все функции обработки ошибок в этой статье (mysqli_connect_error, mysqli_connect_errno, mysqli_error, mysqli_errno) возвращают информацию только о последней ошибке. Но ошибок может быть несколько.
Была ли эта статья полезна?
Есть вопрос?
Закажите недорогой хостинг
Заказать
всего от 290 руб
Библиотека mysqli — своеобразный мостик между процедурным и объектно-ориентированным стилем программирования. Поэтому она поддерживает два подхода обработки ошибок: процедурный и через исключения.
Первый, процедурный подход — вы проверяете результат выполнения каждой функции и если возвращается false
, получаете сообщение при помощи функции mysqli_error()
$db = mysqli_connect("localhost", "user", "...", "test");
if (mysqli_connect_errno()) {
echo "Ошибка установки соединения" . mysqli_connect_error();
exit();
}
$sql = "INSERT INTO users (login, email, password, datetime) VALUES (?, ?, ?, ?)";
$stmt = mysqli_prepare($db, $sql);
if(!$stmt) {
echo "Ошибка подготовки запроса: " . mysqli_error($db);
exit();
}
if(!mysqli_stmt_bind_param($stmt, 'ssss', $login, $email, $password, $datetime)) {
echo "Ошибка связывания параметров: " . mysqli_error($db);
exit();
}
if(!mysqli_stmt_execute($stmt)) {
echo "Ошибка выполнения запроса: " . mysqli_error($db);
exit();
}
mysqli_stmt_close($stmt);
mysqli_close($db);
Разумеется это не очень удобно, особенно, в объектно-ориентированном коде, поэтому mysqli позволяет переключиться в режим генерации исключений (их несколько типов, в режиме отладки наиболее удобно использовать MYSQLI_REPORT_ALL
). Задать режим можно при помощи функции mysqli_report()
mysqli_report(MYSQLI_REPORT_ALL);
try {
$db = mysqli_connect("localhost", "user", "...", "test");
$sql = "INSERT INTO users (login, email, password, datetime) VALUES (?, ?, ?, ?)";
$stmt = mysqli_prepare($db, $sql);
mysqli_stmt_bind_param($stmt, 'ssss', $login, $email, $password, $datetime);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
mysqli_close($db);
} catch (Exception $e) {
echo $e->getMessage();
}
Ошибки перехватываются при помощи стандартного механизма исключений. Не смотря на то, что вы выбрали процедурный стиль, вы можете воспользоваться перехватом исключений, который чаще применяется в объектно-ориентированном коде.
- Исходники
- Исходники PHP
- [PHP] MySQL
- MySQL: Обработка ошибок запросов
Written on 20 Января 2007.
Сообщение о последней ошибке можно получить через функцию mysql_error:
echo «Ошибка базы данных. MySQL пишет:», mysql_error();
Если результат функции пишется в переменную, можно проверить её:
$result = mysql_query($request);if (!$result)
echo "Ошибка базы данных. MySQL пишет:", mysql_error();
else {
echo "<table>";
while ($row = mysql_fetch_array($result))
echo "<tr><td>", $row["field1"], "</td><td>", $row["field2"], "</td></tr>";
echo "</table>";
};
Если в переменную не пишем, то так:
$request = "UPDATE (...)";mysql_query($request);
if (!mysql_error())
echo "Обновление данных прошло успешно!";
else echo "Ошибка базы данных. MySQL пишет:", mysql_error();
Если запрос генерируется автоматически, можно выводить и сам запрос (полезно создавать переменную, которая бы его содержала, и использовать её в качестве параметра функции).