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 WARNINGSSHOW ERRORS查看条件或错误。)

执行GET DIAGNOSTICS不需要特殊特权。

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

GET DIAGNOSTICS语句通常在存储程序的处理程序中使用。这是一个 MySQL 扩展,允许在处理程序上下文之外使用获取[当前]诊断来检查任何 SQL 语句的执行情况。例如,如果调用mysqlClient 端程序,则可以在提示符下 Importing 以下语句:

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' |
+-------+------------------------------------+

该 extensions 仅适用于当前的诊断区域。它不适用于第二个诊断区域,因为仅当当前上下文是条件处理程序时,才允许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的此实例将可用条件的数量和受影响的行数分配给用户变量@p1@p2
GET DIAGNOSTICS @p1 = NUMBER, @p2 = ROW_COUNT;
  • 要获取条件信息,请指定条件编号并将所需的条件项检索到目标变量中。 GET DIAGNOSTICS的此实例将 SQLSTATE 值和错误消息分配给用户变量@p3@p4
GET DIAGNOSTICS CONDITION 1
  @p3 = RETURNED_SQLSTATE, @p4 = MESSAGE_TEXT;

检索列 table 指定一个或多个target = item_name分配,以逗号分隔。每个赋值都指定一个目标变量,并使用* statement_information_item_name condition_information_item_name *指示符,具体取决于语句是检索语句还是条件信息。

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

有效的condition_number 指示符可以是存储过程或函数参数,用DECLARE声明的存储程序局部变量,用户定义的变量,系统变量或 Literals。字符 Literals 可能包含 _charset *引荐。如果条件编号不在 1 到具有信息的条件区域数的范围内,则会发生警告。在这种情况下,警告会添加到诊断区域而不会清除。

发生条件时,MySQL 不会填充GET DIAGNOSTICS识别的所有条件项。例如:

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

在标准 SQL 中,如果存在多个条件,则第一个条件与为先前的 SQL 语句返回的SQLSTATE值有关。在 MySQL 中,无法保证。要获取主要错误,您不能执行以下操作:

GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;

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

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

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

这是一个示例,该示例在存储过程上下文中使用GET DIAGNOSTICS和异常处理程序来评估插入操作的结果。如果插入成功,则该过程使用GET DIAGNOSTICS来获取受影响的行数。这 table 明只要尚未清除当前诊断区域,就可以多次使用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 |
+-------------------------------------------------------------------------+

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

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

  • 可以在处理程序中使用获取[当前]诊断获取堆叠诊断来访问当前和堆叠诊断区域的内容。

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

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

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

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

在存储过程p()中,我们尝试将两个值插入包含TEXT NOT NULL列的 table 中。第一个值是非NULL字符串,第二个值是NULL。该列禁止NULL值,因此第一个插入成功,但是第二个插入导致异常。该过程包括一个异常处理程序,该处理程序 Map 将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插入 Map 到空字符串插入并显示结果。新插入成功并清除了当前的诊断区域,但是堆叠的诊断区域保持不变,并且仍然包含有关激活处理程序的条件的信息:

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

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

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

该过程返回后,该 table 包含两行。空行是由尝试插入 Map 到空字符串插入的NULL导致的:

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

在前面的示例中,条件处理程序中从当前和堆叠的诊断区域检索信息的前两个GET DIAGNOSTICS语句返回相同的值。如果重置当前诊断区域的语句在处理程序中较早执行,则情况并非如此。假设p()被重写以将DECLARE语句放置在处理程序定义中而不是放在其前面:

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 5.7.2 之前,DECLARE不会更改当前的诊断区域,因此前两个GET DIAGNOSTICS语句返回相同的结果,就像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 |
+---------------------------------+-------+----------------------------+

为避免在条件处理程序中寻求获取有关激活该处理程序的条件的信息时发生此问题,请确保访问堆叠的诊断区域,而不是当前的诊断区域。