> For the complete documentation index, see [llms.txt](https://hezhiqiang-book.gitbook.io/mysql/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hezhiqiang-book.gitbook.io/mysql/di-yi-zhang/index-10/xu-lie.md).

# 11.4 序列

在本教程中，我们将向您展示如何使用MySQL序列为表的ID列自动生成唯一编号。

## 创建MySQL序列

在MySQL中，序列是以升序生成的[整数](http://www.yiibai.com/mysql/int.html)列表，即`1`,`2`,`3` `...`许多应用程序需要序列来生成主要用于识别的唯一数字，例如：CRM中的客户ID，HR中的员工编号，服务器管理系统的设备编号等。

要自动在MySQL中创建序列，可以在列上设置`AUTO_INCREMENT`属性，这通常是[主键](http://www.yiibai.com/mysql/primary-key.html)列。

使用`AUTO_INCREMENT`属性时，将应用以下规则：

* 每个表只有一个`AUTO_INCREMENT`列，其数据类型通常为整数。
* 必须对`AUTO_INCREMENT`列进行索引，它可以是[PRIMARY KEY](http://www.yiibai.com/mysql/primary-key.html)或[UNIQUE](http://www.yiibai.com/mysql/unique.html)索引。
* `AUTO_INCREMENT`列必须具有[NOT NULL约束](http://www.yiibai.com/mysql/not-null-constraint.html)。当您为列设置`AUTO_INCREMENT`属性时，MySQL会自动将`NOT NULL`约束隐式添加到列中。

**创建MySQL序列示例**

以下语句创建一个名为`employees`的表，其`emp_no`列为`AUTO_INCREMENT`列：

```sql
USE testdb;
CREATE TABLE employees(
    emp_no INT(4) AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(50),
    last_name  VARCHAR(50)
);
```

## MySQL序列如何工作

`AUTO_INCREMENT`列具有以下属性：

* `AUTO_INCREMENT`列的起始值为`1`，当您向列中插入[NULL](http://www.yiibai.com/mysql/null.html)值或在[INSERT](http://www.yiibai.com/mysql/insert-statement.html)语句中省略其值时，它将增加`1`。
* 要获取最后生成的序列号，请使用[LAST\_INSERT\_ID()函数](http://www.yiibai.com/mysql/last_insert_id.html)。 我们经常要后续语句中使用最后一个插入ID，例如将数据插入到表中。 最后生成的序列在会话中是唯一的。 换句话说，如果另一个连接生成序列号，从连接中可以使用`LAST_INSERT_ID()`函数获取它。
* 如果将[新行插入到表中](http://www.yiibai.com/mysql/insert-statement.html)并指定序列列的值，如果序列号不存在于列中，则MySQL将插入序列号，如果序列号已存在，则会发出错误。 如果插入大于下一个序列号的新值，MySQL将使用新值作为起始序列号，并生成大于当前值的唯一序列号。这会在序列中产生一段空白(不连续)。
* 如果使用[UPDATE](http://www.yiibai.com/mysql/update-data.html)语句将`AUTO_INCREMENT`列中的值更新为已存在的值，如果该列具有唯一索引，则MySQL将发出重复键错误。 如果将`AUTO_INCREMENT`列更新为大于列中现有值的值，MySQL将使用最后一个插入序列号加`1`的值作为下一行列号值。 例如，如果最后一个插入序列号为`3`，然后又将其更新为`10`，那么新插入行的序列号不是`11`，而是`4`。
* 如果使用[DELETE](http://www.yiibai.com/mysql/delete-statement.html)语句删除最后插入的行，MySQL可能会也可能不会根据表的[存储引擎](http://www.yiibai.com/understand-mysql-table-types-innodb-myisam.html)重复使用已删除的序列号。 如果您删除一行，则*MyISAM*表不会重复使用已删除的序列号，例如，如果删除表中的最后一个插入`ID`为`10`，则MySQL仍会为新行生成*11*个下一个序列号。 与*MyISAM*表类似，*InnoDB*表在行被删除时不重复使用序列号。

在列上设置*AUTO\_INCREMENT*属性后，可以以各种方式[重置自动增量值](http://www.yiibai.com/mysql/reset-auto-increment.html)，例如使用[ALTER TABLE](http://www.yiibai.com/mysql/alter-table.html)语句。

我们来看一下一些例子来更好地了解MySQL序列。

**第一步**，在`employees`表中插入两行：

```sql
INSERT INTO employees(first_name,last_name)
VALUES('John','Doe'),
      ('Mary','Jane');
```

**第二步**，从`employees`表中查询选择数据：

```sql
mysql> SELECT * FROM employees;
+--------+------------+-----------+
| emp_no | first_name | last_name |
+--------+------------+-----------+
|      1 | John       | Doe       |
|      2 | Mary       | Jane      |
+--------+------------+-----------+
2 rows in set
```

**第三步**，删除`emp_no`为`2`的第二个员工信息：

```sql
mysql> DELETE FROM employees
WHERE emp_no = 2;
Query OK, 1 row affected

mysql> SELECT * FROM employees;
+--------+------------+-----------+
| emp_no | first_name | last_name |
+--------+------------+-----------+
|      1 | John       | Doe       |
+--------+------------+-----------+
1 row in set
```

**第四步**，插入新员工，并查询最后一位员工信息(`emp_no`)：

```sql
mysql> INSERT INTO employees(first_name,last_name)
VALUES('Jack','Lee');
Query OK, 1 row affected

mysql> SELECT * FROM employees;
+--------+------------+-----------+
| emp_no | first_name | last_name |
+--------+------------+-----------+
|      1 | John       | Doe       |
|      3 | Jack       | Lee       |
+--------+------------+-----------+
2 rows in set
```

因为`employees`表的存储引擎是*InnoDB*，它不会重复使用已删除的序列号。 新行使用`emp_no`的值是`3`。

**第五步**，将`emp_no = 3`已存在新员工更新为`emp_no = 1`：

```sql
UPDATE employees
SET first_name = 'Joe',
    emp_no = 1
WHERE emp_no = 3;
```

上面语句执行时，MySQL发出主键重复条目的错误。如何来解决它？

```sql
UPDATE employees
SET first_name = 'Joe',
    emp_no = 10
WHERE emp_no = 3;
```

执行结果如下 -

```sql
mysql> UPDATE employees
SET first_name = 'Joe',
    emp_no = 10
WHERE emp_no = 3;
Query OK, 1 row affected
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM employees;
+--------+------------+-----------+
| emp_no | first_name | last_name |
+--------+------------+-----------+
|      1 | John       | Doe       |
|     10 | Joe        | Lee       |
+--------+------------+-----------+
2 rows in set
```

上面步骤中，将序列号更新为`10`。

**第六**，插入新员工数据 -

```sql
mysql> INSERT INTO employees(first_name,last_name)
VALUES('Wang','Lee');
Query OK, 1 row affected

mysql> SELECT * FROM employees;
+--------+------------+-----------+
| emp_no | first_name | last_name |
+--------+------------+-----------+
|      1 | John       | Doe       |
|      4 | Wang       | Lee       |
|     10 | Joe        | Lee       |
+--------+------------+-----------+
```

最后插入的下一个序列号是`4`，因此，MySQL使用数字是`4`作为新行序列值，而不是`11`。

在本教程中，您已经学习了如何使用MySQL序列为主键列生成唯一的编号，方法是为列设置`AUTO_INCREMENT`属性。
