#### 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.html)语句使应用程序能够检查此信息。（您也可以使用[`显示警告`](show-warnings.html)要么[`显示错误`](show-errors.html)查看条件或错误。）

执行不需要特殊权限[`得到诊断`](get-diagnostics.html).

关键字`当前的`表示从当前诊断区域检索信息。关键字`堆叠`表示从第二个诊断区域检索信息，该区域仅在当前上下文是条件处理程序时可用。如果没有给出任何关键字，则默认使用当前诊断区域。

这[`得到诊断`](get-diagnostics.html)语句通常用在存储程序中的处理程序中。这是一个 MySQL 扩展[`获取 [当前] 诊断信息`](get-diagnostics.html)允许在处理程序上下文之外检查任何 SQL 语句的执行。例如，如果您调用[**mysql**](mysql.html)客户端程序，您可以在提示符下输入以下语句：

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

此扩展仅适用于当前诊断区域。它不适用于第二个诊断区域，因为`获得叠加诊断`仅当当前上下文是条件处理程序时才允许。如果不是这样，一个`处理程序不活动时获取堆叠诊断`发生错误。

有关诊断区域的说明，请参见[第 13.6.7.7 节，“MySQL 诊断区域”](diagnostics-area.html).简而言之，它包含两种信息：

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

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

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


```
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.html)可以获取语句或条件信息，但不能在同一语句中同时获取：

-   要获取语句信息，请将所需语句项检索到目标变量中。这个实例[`得到诊断`](get-diagnostics.html)将可用条件的数量和受影响的行数分配给用户变量`@p1`和`@p2`：

    ```
    GET DIAGNOSTICS @p1 = NUMBER, @p2 = ROW_COUNT;
    ```

-   要获取条件信息，请指定条件编号并将所需条件项检索到目标变量中。这个实例[`得到诊断`](get-diagnostics.html)将 SQLSTATE 值和错误消息分配给用户变量`@p3`和`@p4`：

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

    检索列表指定一个或多个`*`目标`* = *`项目名称`*`作业，用逗号分隔。每个赋值命名一个目标变量和一个*`statement_information_item_name`*要么*`condition_information_item_name`*指示符，取决于语句是检索语句还是条件信息。

    有效的*`目标`*用于存储项目信息的指示符可以是存储过程或函数参数、使用声明的存储程序局部变量[`宣布`](declare.html)，或用户定义的变量。

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

    当一个条件发生时，MySQL 不会填充所有被识别的条件项[`得到诊断`](get-diagnostics.html).例如：


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

有关允许的语句和条件信息项的信息，以及在条件发生时填充哪些信息，请参阅[诊断区域信息项](diagnostics-area.html#diagnostics-area-information-items).

这是一个使用的示例[`得到诊断`](get-diagnostics.html)以及存储过程上下文中的异常处理程序，以评估插入操作的结果。如果插入成功，则程序使用[`得到诊断`](get-diagnostics.html)获取受影响的行数。这表明您可以使用[`得到诊断`](get-diagnostics.html)只要当前诊断区域未被清除，多次检索有关语句的信息。

```
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`是一个整数列，声明为`非空`.该过程在调用插入非`空值`和`空值`值，分别为：

```
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-diagnostics.html)和[`获得叠加诊断`](get-diagnostics.html)可以在处理程序中使用以访问当前和堆叠诊断区域的内容。

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

-   但是，在处理程序中执行的语句可以修改当前诊断区域，根据正常规则清除和设置其内容（参见[如何清除和填充诊断区域](diagnostics-area.html#diagnostics-area-populating)）。

    获取有关处理程序激活条件的信息的更可靠方法是使用堆叠诊断区域，该区域不能被处理程序中执行的语句修改，除非[`重新信号`](resignal.html).有关何时设置和清除当前诊断区域的信息，请参阅[第 13.6.7.7 节，“MySQL 诊断区域”](diagnostics-area.html).

    下一个示例显示了如何`获得叠加诊断`可以在处理程序中使用以获取有关已处理异常的信息，即使在当前诊断区域已被处理程序语句修改后也是如此。

    在存储过程中`p()`，我们尝试将两个值插入到包含`文本不为空`柱子。第一个值是非`空值`字符串，第二个是`空值`.该栏目禁止`空值`值，所以第一次插入成功，但第二次导致异常。该过程包括一个映射尝试插入的异常处理程序`空值`插入空字符串：


```
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.html)语句可能会重置当前的诊断区域。语句可能会重置当前的诊断区域。例如，处理程序映射`空值`插入到空字符串插入并显示结果。新插入成功并清除当前诊断区域，但堆叠的诊断区域保持不变，仍然包含有关激活处理程序的条件的信息：

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

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

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

过程返回后，表包含两行。空行是由于尝试插入`空值`映射到空字符串插入：

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

在前面的例子中，前两个[`得到诊断`](get-diagnostics.html)从当前和堆叠诊断区域检索信息的条件处理程序中的语句返回相同的值。如果重置当前诊断区域的语句在处理程序中较早执行，则情况并非如此。假设`p()`被重写以放置[`宣布`](declare.html)处理程序定义中的语句而不是它之前的语句：

```
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.html)不会改变当前的诊断区域，所以前两个[`得到诊断`](get-diagnostics.html)语句返回相同的结果，就像在原始版本中一样`p()`.

    在 MySQL 5.7.2 中，根据 SQL 标准，已完成工作以确保所有非诊断语句填充诊断区域。[`宣布`](declare.html)是其中之一，因此在 5.7.2 及更高版本中，[`宣布`](declare.html)在处理程序开头执行的语句清除当前诊断区域和[`得到诊断`](get-diagnostics.html)语句产生不同的结果：

    ```
    +---------------------------------+-------+------+
    | op                              | errno | msg  |
    +---------------------------------+-------+------+
    | current DA before mapped insert |  NULL | NULL |
    +---------------------------------+-------+------+

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

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