# 游标

在本教程中，您将学习如何在存储过程中使用MySQL游标来遍历`SELECT`语句返回的结果集。

## MySQL游标简介

要处理[存储过程](http://www.yiibai.com/mysql/stored-procedure.html)中的结果集，请使用游标。游标允许您迭代查询返回的一组行，并相应地处理每行。

MySQL游标为只读，不可滚动和敏感。

* **只读**：无法通过光标更新基础表中的数据。
* **不可滚动**：只能按照[SELECT](http://www.yiibai.com/mysql/select-statement-query-data.html)语句确定的顺序获取行。不能以相反的顺序获取行。 此外，不能跳过行或跳转到结果集中的特定行。
* **敏感**：有两种游标：敏感游标和不敏感游标。敏感游标指向实际数据，不敏感游标使用数据的临时副本。敏感游标比一个不敏感的游标执行得更快，因为它不需要临时拷贝数据。但是，对其他连接的数据所做的任何更改都将影响由敏感游标使用的数据，因此，如果不更新敏感游标所使用的数据，则更安全。 MySQL游标是敏感的。

您可以在[存储过程](http://www.yiibai.com/mysql/stored-procedure.html)，[存储函数](http://www.yiibai.com/mysql/stored-function.html)和[触发器](http://www.yiibai.com/mysql/triggers.html)中使用MySQL游标。

## 使用MySQL游标

首先，必须使用`DECLARE`语句声明游标：

```sql
DECLARE cursor_name CURSOR FOR SELECT_statement;
```

游标声明必须在[变量](http://www.yiibai.com/mysql/variables-in-stored-procedures.html)声明之后。如果在变量声明之前声明游标，MySQL将会发出一个错误。游标必须始终与`SELECT`语句相关联。

接下来，使用`OPEN`语句打开游标。`OPEN`语句初始化游标的结果集，因此您必须在从结果集中提取行之前调用`OPEN`语句。

```sql
OPEN cursor_name;
```

然后，使用`FETCH`语句来检索光标指向的下一行，并将光标移动到结果集中的下一行。

```sql
FETCH cursor_name INTO variables list;
```

之后，可以检查是否有任何行记录可用，然后再提取它。

最后，调用`CLOSE`语句来停用光标并释放与之关联的内存，如下所示：

```sql
CLOSE cursor_name;
```

当光标不再使用时，应该关闭它。

当使用MySQL游标时，还必须声明一个`NOT FOUND`处理程序来处理当游标找不到任何行时的情况。 因为每次调用`FETCH`语句时，游标会尝试读取结果集中的下一行。 当光标到达结果集的末尾时，它将无法获得数据，并且会产生一个条件。 处理程序用于处理这种情况。

要声明一个`NOT FOUND`处理程序，参考以下语法：

```sql
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
```

`finished`是一个变量，指示光标到达结果集的结尾。请注意，处理程序声明必须出现在存储过程中的变量和游标声明之后。

下图说明了MySQL游标如何工作。

![img](/files/-LyNv2CMxtO2bExMdfr7)

## MySQL游标示例

为了更好地演示，我们将开发一个存储过程，来获取MySQL[示例数据库(yiibaidb)](http://www.yiibai.com/mysql/sample-database.html)中`employees`表中所有员工的电子邮件列表。

首先，声明一些变量，一个用于循环员工电子邮件的游标和一个`NOT FOUND`处理程序：

```sql
DECLARE finished INTEGER DEFAULT 0;
DECLARE email varchar(255) DEFAULT "";

-- declare cursor for employee email
DEClARE email_cursor CURSOR FOR 
 SELECT email FROM employees;

-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER 
FOR NOT FOUND SET finished = 1;
```

接下来，使用`OPEN`语句打开`email_cursor`：

```sql
OPEN email_cursor;
```

然后，迭代电子邮件列表，并使用分隔符(`;`)[连接](http://www.yiibai.com/mysql/sql-concat-in-mysql.html)每个电子邮件：

```sql
get_email: LOOP
 FETCH email_cursor INTO v_email;
 IF v_finished = 1 THEN 
 LEAVE get_email;
 END IF;
 -- build email list
 SET email_list = CONCAT(v_email,";",email_list);
END LOOP get_email;
```

之后，在循环中，使用`v_finished`变量来检查列表中是否有任何电子邮件来终止循环。

最后，使用`CLOSE`语句关闭游标：

```sql
CLOSE email_cursor;
```

`build_email_list`存储过程所有代码如下：

```sql
DELIMITER $$

CREATE PROCEDURE build_email_list (INOUT email_list varchar(4000))
BEGIN

 DECLARE v_finished INTEGER DEFAULT 0;
        DECLARE v_email varchar(100) DEFAULT "";

 -- declare cursor for employee email
 DEClARE email_cursor CURSOR FOR 
 SELECT email FROM employees;

 -- declare NOT FOUND handler
 DECLARE CONTINUE HANDLER 
        FOR NOT FOUND SET v_finished = 1;

 OPEN email_cursor;

 get_email: LOOP

 FETCH email_cursor INTO v_email;

 IF v_finished = 1 THEN 
 LEAVE get_email;
 END IF;

 -- build email list
 SET email_list = CONCAT(v_email,";",email_list);

 END LOOP get_email;

 CLOSE email_cursor;

END$$

DELIMITER ;
```

可以使用以下脚本测试`build_email_list`存储过程：

```sql
SET @email_list = "";
CALL build_email_list(@email_list);
SELECT @email_list;
```

> 注：由于内容比较长，这里就不放上输出结果了。

在本教程中，我们向您展示了如何使用MySQL游标来迭代结果集并相应地处理每一行。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hezhiqiang-book.gitbook.io/mysql/di-san-zhang/you-biao.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
