Documentation Home
MySQL 8.0 参考手册  / 第 13 章 SQL 语句  / 13.6 复合语句语法  / 13.6.7 条件处理  /  13.6.7.3 GET DIAGNOSTICS 语句

13.6.7.3 GET DIAGNOSTICS 语句

GET [CURRENT | STACKED] DIAGNOSTICS {
    statement_information_item
    [, statement_information_item] ...
  | CONDITION condition_number
    condition_information_item
    [, condition_information_item] ...
}

statement_information_item:
    target = statement_information_item_name

condition_information_item:
    target = condition_information_item_name

statement_information_item_name: {
    NUMBER
  | ROW_COUNT
}

condition_information_item_name: {
    CLASS_ORIGIN
  | SUBCLASS_ORIGIN
  | RETURNED_SQLSTATE
  | MESSAGE_TEXT
  | MYSQL_ERRNO
  | CONSTRAINT_CATALOG
  | CONSTRAINT_SCHEMA
  | CONSTRAINT_NAME
  | CATALOG_NAME
  | SCHEMA_NAME
  | TABLE_NAME
  | COLUMN_NAME
  | CURSOR_NAME
}

condition_number, target:
    (see following discussion)

SQL 语句产生填充诊断区域的诊断信息。该GET DIAGNOSTICS语句使应用程序能够检查此信息。(您也可以使用SHOW WARNINGSorSHOW ERRORS 来查看条件或错误。)

执行不需要特殊权限 GET DIAGNOSTICS

关键字CURRENT表示从当前诊断区域检索信息。该关键字 STACKED表示从第二个诊断区域检索信息,仅当当前上下文是条件处理程序时才可用。如果两个关键字都没有给出,则默认使用当前诊断区域。

GET DIAGNOSTICS语句通常用于存储程序中的处理程序。它是一个 MySQL 扩展, GET [CURRENT] DIAGNOSTICS允许在处理程序上下文之外检查任何 SQL 语句的执行。例如调用mysql客户端程序,可以在提示符下输入这些语句:

mysql> DROP TABLE test.no_such_table;
ERROR 1051 (42S02): Unknown table 'test.no_such_table'
mysql> GET DIAGNOSTICS CONDITION 1
         @p1 = RETURNED_SQLSTATE, @p2 = MESSAGE_TEXT;
mysql> SELECT @p1, @p2;
+-------+------------------------------------+
| @p1   | @p2                                |
+-------+------------------------------------+
| 42S02 | Unknown table 'test.no_such_table' |
+-------+------------------------------------+

此扩展仅适用于当前诊断区域。它不适用于第二个诊断区域,因为 GET STACKED DIAGNOSTICS仅当当前上下文是条件处理程序时才允许。如果不是这种情况,GET STACKED DIAGNOSTICS when handler not active则会发生错误。

有关诊断区域的描述,请参阅 第 13.6.7.7 节,“MySQL 诊断区域”。简而言之,它包含两种信息:

  • 语句信息,例如发生的条件数或受影响的行数。

  • 条件信息,例如错误代码和消息。如果语句引发多个条件,则诊断区域的这一部分对每个条件都有一个条件区域。如果语句未引发任何条件,则诊断区域的这一部分为空。

对于产生三个条件的语句,诊断区域包含如下语句和条件信息:

Statement information:
  row count
  ... other statement information items ...
Condition area list:
  Condition area 1:
    error code for condition 1
    error message for condition 1
    ... other condition information items ...
  Condition area 2:
    error code for condition 2:
    error message for condition 2
    ... other condition information items ...
  Condition area 3:
    error code for condition 3
    error message for condition 3
    ... other condition information items ...

GET DIAGNOSTICS可以获取语句或条件信息,但不能在同一语句中同时获取两者:

  • 要获取语句信息,请将所需的语句项检索到目标变量中。此实例 GET DIAGNOSTICS将可用条件的数量和受影响的行数分配给用户变量@p1and @p2

    GET DIAGNOSTICS @p1 = NUMBER, @p2 = ROW_COUNT;
  • 要获取条件信息,请指定条件编号并将所需的条件项检索到目标变量中。此实例GET DIAGNOSTICS将 SQLSTATE 值和错误消息分配给用户变量@p3并且 @p4

    GET DIAGNOSTICS CONDITION 1
      @p3 = RETURNED_SQLSTATE, @p4 = MESSAGE_TEXT;

检索列表指定一个或多个 分配,以逗号分隔。每个赋值命名一个目标变量和一个 或 指示符,这取决于该语句是检索语句信息还是条件信息。 target = item_namestatement_information_item_namecondition_information_item_name

用于存储项目信息的有效target指示符可以是存储过程或函数参数、用 声明的存储程序局部变量 DECLARE或用户定义的变量。

有效condition_number的指示符可以是存储过程或函数参数、用 声明的存储程序局部变量 DECLARE、用户定义的变量、系统变量或文字。字符字面量可以包括 _charset介绍人。如果条件编号不在从 1 到具有信息的条件区域数的范围内,则会出现警告。在这种情况下,警告会添加到诊断区域而不清除它。

当条件发生时,MySQL 不会填充GET DIAGNOSTICS. 例如:

mysql> GET DIAGNOSTICS CONDITION 1
         @p5 = SCHEMA_NAME, @p6 = TABLE_NAME;
mysql> SELECT @p5, @p6;
+------+------+
| @p5  | @p6  |
+------+------+
|      |      |
+------+------+

在标准 SQL 中,如果有多个条件,则第一个条件与SQLSTATE前一个 SQL 语句的返回值相关。在 MySQL 中,这是无法保证的。要获得主要错误,您不能这样做:

GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;

相反,首先检索条件计数,然后使用它指定要检查的条件数:

GET DIAGNOSTICS @cno = NUMBER;
GET DIAGNOSTICS CONDITION @cno @errno = MYSQL_ERRNO;

有关允许的语句和条件信息项的信息,以及在条件发生时填充哪些信息项,请参阅 诊断区域信息项

下面是一个示例,它GET DIAGNOSTICS在存储过程上下文中使用异常处理程序来评估插入操作的结果。如果插入成功,该过程将 GET DIAGNOSTICS用于获取受影响的行数。这表明 GET DIAGNOSTICS只要当前诊断区域未被清除,您就可以使用多次来检索有关语句的信息。

CREATE PROCEDURE do_insert(value INT)
BEGIN
  -- Declare variables to hold diagnostics area information
  DECLARE code CHAR(5) DEFAULT '00000';
  DECLARE msg TEXT;
  DECLARE nrows 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 nrows = ROW_COUNT;
    SET result = CONCAT('insert succeeded, row count = ',nrows);
  ELSE
    SET result = CONCAT('insert failed, error = ',code,', message = ',msg);
  END IF;
  -- Say what happened
  SELECT result;
END;

假设那t1.int_col是一个声明为的整数列NOT NULL。该过程在被调用以分别插入非值NULLNULL值时产生这些结果:

mysql> CALL do_insert(1);
+---------------------------------+
| result                          |
+---------------------------------+
| insert succeeded, row count = 1 |
+---------------------------------+

mysql> CALL do_insert(NULL);
+-------------------------------------------------------------------------+
| result                                                                  |
+-------------------------------------------------------------------------+
| insert failed, error = 23000, message = Column 'int_col' cannot be null |
+-------------------------------------------------------------------------+

当条件处理程序激活时,将推送到诊断区域堆栈:

  • 第一个(当前)诊断区域成为第二个(堆叠)诊断区域,并创建一个新的当前诊断区域作为它的副本。

  • GET [CURRENT] DIAGNOSTICS并且 GET STACKED DIAGNOSTICS可以在处理程序中使用以访问当前和堆栈诊断区域的内容。

  • 最初,两个诊断区域返回相同的结果,因此只要您在处理程序中不执行任何更改其当前诊断区域的语句 ,就可以从当前诊断区域获取有关激活处理程序的条件的信息。

  • 但是,在处理程序中执行的语句可以修改当前诊断区域,根据正常规则清除和设置其内容(请参阅 如何清除和填充诊断区域)。

    获取有关处理程序激活条件的信息的更可靠方法是使用堆栈诊断区域,该区域不能被处理程序中执行的语句修改,除了 RESIGNAL. 有关何时设置和清除当前诊断区域的信息,请参阅第 13.6.7.7 节,“MySQL 诊断区域”

下一个示例显示如何GET STACKED DIAGNOSTICS在处理程序中使用它来获取有关已处理异常的信息,即使在当前诊断区域已被处理程序语句修改之后。

在存储过程p()中,我们试图将两个值插入到包含一TEXT NOT NULL列的表中。第一个值是非NULL字符串,第二个是 NULL。该列禁止 NULL值,因此第一次插入成功但第二次插入导致异常。该过程包括一个异常处理程序,它将插入尝试映射到 NULL空字符串的插入中:

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;

当处理程序激活时,当前诊断区域的副本被推送到诊断区域堆栈。处理程序首先显示当前和堆栈诊断区域的内容,它们最初是相同的:

+---------------------------------+-------+----------------------------+
| op                              | errno | msg                        |
+---------------------------------+-------+----------------------------+
| current DA before mapped insert |  1048 | Column 'c1' cannot be null |
+---------------------------------+-------+----------------------------+

+---------------------------------+-------+----------------------------+
| op                              | errno | msg                        |
+---------------------------------+-------+----------------------------+
| stacked DA before mapped insert |  1048 | Column 'c1' cannot be null |
+---------------------------------+-------+----------------------------+

在语句之后执行的GET DIAGNOSTICS语句可能会重置当前诊断区域。语句可能会重置当前的诊断区域。例如,处理程序将NULL 插入映射到空字符串插入并显示结果。新插入成功并清除当前诊断区域,但堆栈诊断区域保持不变,仍然包含有关激活处理程序的条件的信息:

+----------------------------------------------+
| op                                           |
+----------------------------------------------+
| mapped insert succeeded, current DA is empty |
+----------------------------------------------+

+--------------------------------+-------+----------------------------+
| op                             | errno | msg                        |
+--------------------------------+-------+----------------------------+
| stacked DA after mapped insert |  1048 | Column 'c1' cannot be null |
+--------------------------------+-------+----------------------------+

当条件处理程序结束时,它的当前诊断区域将从堆栈中弹出,堆栈诊断区域成为存储过程中的当前诊断区域。

过程返回后,该表包含两行。NULL空行是由映射到空字符串插入 的尝试插入 引起的:

+----------+
| c1       |
+----------+
| string 1 |
|          |
+----------+

In the preceding example, the first two GET DIAGNOSTICS statements within the condition handler that retrieve information from the current and stacked diagnostics areas return the same values. This is not the case if statements that reset the current diagnostics area executed earlier within the handler. Suppose that p() is rewritten to place the DECLARE statements within the handler definition rather than preceding it:

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;
...

In this case, the result is version dependent:

  • Before MySQL 5.7.2, DECLARE does not change the current diagnostics area, so the first two GET DIAGNOSTICS statements return the same result, just as in the original version of p().

    在 MySQL 5.7.2 中,已完成工作以确保所有非诊断语句根据 SQL 标准填充诊断区域。DECLARE是其中之一,因此在 5.7.2 及更高版本中, DECLARE在处理程序开头执行的语句会清除当前诊断区域,并且这些GET DIAGNOSTICS语句会产生不同的结果:

    +---------------------------------+-------+------+
    | op                              | errno | msg  |
    +---------------------------------+-------+------+
    | current DA before mapped insert |  NULL | NULL |
    +---------------------------------+-------+------+
    
    +---------------------------------+-------+----------------------------+
    | op                              | errno | msg                        |
    +---------------------------------+-------+----------------------------+
    | stacked DA before mapped insert |  1048 | Column 'c1' cannot be null |
    +---------------------------------+-------+----------------------------+

在寻求获取有关激活处理程序的条件的信息时,要避免在条件处理程序中出现此问题,请务必访问堆栈诊断区域,而不是当前诊断区域。