Mysql обработчик ошибок

В этой статье мы расскажем вам, как использовать обработчик 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, хранимая процедура продолжает исполняться.

В результате, мы все равно получили список тегов для статьи:

Пример обработчика MySQL в хранимых процедурах

Если мы в объявлении обработчика изменим команду 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 в хранимых процедурах - 2

В случае если у вас есть несколько обработчиков, которые имеют право обрабатывать ошибку, 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:

Приоритет обработчиков 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 ( BEGINEND ) 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 an SQLWARNING , NOTFOUND or SQLEXCEPTION condition, which is shorthand for the class of SQLSTATE values. The NOTFOUND condition is used for a cursor or  SELECT 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 SupplierProductsfor 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 an SQLWARNING is the shorthand for a class of SQLSTATES 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, хранимая процедура продолжает исполняться.

В результате, мы все равно получили список тегов для статьи:

Пример обработчика MySQL в хранимых процедурах

Если мы в объявлении обработчика изменим команду 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 в хранимых процедурах - 2

В случае если у вас есть несколько обработчиков, которые имеют право обрабатывать ошибку, 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:

Приоритет обработчиков 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();

Если запрос генерируется автоматически, можно выводить и сам запрос (полезно создавать переменную, которая бы его содержала, и использовать её в качестве параметра функции).

Понравилась статья? Поделить с друзьями:
  • Mysql ошибка 1226
  • Mysql ошибка 121
  • Mysql ошибка 1146 как исправить
  • Mtco handling ивеко стралис ошибка
  • Mysql ошибка 1101