MySQL 数据库:字符串数据类型 ENUM

参考

项目描述
MySQL 官方文档The ENUM Type
搜索引擎GoogleBing

描述

项目描述
DataGrip2023.1.1
MySQL8.0.33 MySQL Community Server - GPL

枚举类型

枚举类型

在 MySQL 中,枚举类型属于字符串类型的一种,但是与一般的字符串类型不同,它具有一个预定义的值域。在创建表时,您可以指定枚举类型的可能值,然后在 插入更新 数据时,只能选择这些预定义值之一。

创建枚举类型

在 MySQL 中,可以使用 ENUM 关键字来创建枚举类型。下面是创建枚举类型的基本语法:

CREATE TABLE table_name (
    column_name ENUM('value1', 'value2', 'value3', ...)
);

其中:

  1. table_name 是要创建的表的名称,column_name 是要创建的枚举列的名称。在 ENUM 关键字后面的括号中,列出了枚举类型的预定义值,以逗号分隔。

  2. 在 MySQL 中,ENUM 类型的预定义值的数量是有限的,最多可以有 65535 个预定义值。

举个栗子

# 创建一个数据库用于测试
CREATE DATABASE db_test;

# 使用数据库 db_test
USE db_test;

# 在当前数据库中创建表 tb
CREATE TABLE tb
(
    name VARCHAR(15),
    # 创建一个枚举列,该列仅支持使用预定义值 male 和 female
    sex  ENUM ('male', 'female')
);

# 向表 tb 中插入数据
INSERT INTO tb
VALUES ('RedHeart', 'male'),
       ('TwoMoons', 'female');

# 从表 tb 中查询所有数据
SELECT *
FROM tb;

查询结果

+----------+--------+
| name     | sex    |
+----------+--------+
| RedHeart | male   |
| TwoMoons | female |
+----------+--------+
2 rows in set (0.00 sec)

注:

枚举类型的预定义值仅支持为 字符串,使用其他类型的值作为枚举类型的预定义值时,MySQL 将抛出异常错误。

优劣

优点
优点描述
数据完整性枚举类型限制列中存储的值仅限于预定义的选项之一,确保数据的完整性,防止插入或更新不正确的值
易于理解和维护枚举类型使用预定义的选项,使 数据的含义更加清晰和易于理解。开发人员可以轻松查看和理解列中的可能值,从而简化数据库的维护。
性能优化枚举类型的内部实现使用整数值表示选项,相比于存储字符串值,它可以提供更好的性能。枚举类型所需的存储空间更小,并且在比较和索引操作中更高效。
缺点

如果您预计将来可能需要更改或添加选项,枚举类型可能不太适合,因为在枚举类型中 添加或修改预定义值需要更改表结构

修改表结构的过程可能会引入一些复杂性和风险,特别是在有大量数据和与枚举列相关联的其他表之间存在关联关系时。因此,在预计将来可能需要更改或添加选项的情况下,使用枚举类型可能并不是最佳选择。

相比之下,如果您使用 查找表标志位(BIT) 等其他方法来处理动态选项,您可以更轻松地进行更改和添加选项,而无需修改表结构。这些方法提供了更大的灵活性和可扩展性,适用于变化频繁的选项。

枚举值与其索引

映射

在 MySQL 中,枚举类型中的枚举值对应的索引是指每个枚举值在枚举类型内部的 位置。当定义枚举类型时,每个枚举值都被映射到一个 整数值,这个整数值就是枚举值的索引。

默认情况下,MySQL会将第一个枚举值的索引设置为 1,第二个枚举值的索引设置为 2,依此类推。在内部,MySQL 使用这些整数值来存储和比较枚举列的值

检索索引

如果您在数值上下文中检索枚举值,将返回列值的索引。例如,您可以像这样从枚举列中检索数字值:

SELECT enum_col+0 FROM tbl_name;

举个栗子

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    name VARCHAR(15),
    sex  ENUM ('male', 'female')
);

INSERT INTO tb
VALUES ('RedHeart', 'male'),
       ('TwoMoons', 'female');


SELECT name, sex
FROM tb;

SELECT name, sex + 0
FROM tb;

查询结果

# SELECT name, sex
# FROM tb;
+----------+--------+
| name     | sex    |
+----------+--------+
| RedHeart | male   |
| TwoMoons | female |
+----------+--------+
2 rows in set (0.00 sec)

# SELECT name, sex + 0
# FROM tb;
+----------+---------+
| name     | sex + 0 |
+----------+---------+
| RedHeart |       1 |
| TwoMoons |       2 |
+----------+---------+
2 rows in set (0.00 sec)

插入的数值与数值字符串都将被解释为索引

在 MySQL 中,对于 ENUM 列,插入的值可以是一个数值或数值字符串(使用引号包裹的数值),而不仅仅是非数值字符串。

当插入的值是一个 数值字符串数值 时,MySQL会尝试将其转化为一个整数,并将其视为枚举值的索引。

举个栗子

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    name VARCHAR(15),
    sex  ENUM ('male', 'female')
);

# 将数值或数值字符串插入枚举列时,
# 数值与数值字符串都将被解析为枚举值的索引。
INSERT INTO tb
VALUES ('RedHeart', 1),
       ('TwoMoons', '2');

SELECT *
FROM tb;

执行效果

+----------+--------+
| name     | sex    |
+----------+--------+
| RedHeart | male   |
| TwoMoons | female |
+----------+--------+
2 rows in set (0.00 sec)

由枚举值索引带来的优势

  1. 存储空间
    由于枚举类型内部使用 整数值 来表示枚举值,相比存储相应的字符串值,它可以 节省存储空间。整数值通常需要较少的字节数来存储。

  2. 比较和排序效率
    由于枚举值被映射到整数索引,比较排序 枚举列的值通常比直接比较和排序字符串值更高效。整数比较是直接的数值比较(相比之下,字符串比较需要比较每个字符转换为编码的结果),速度更快。

需要注意的是,虽然枚举值的索引对于 MySQL 内部处理枚举类型很重要,但在使用枚举列时,我们不需要直接操作这些索引。我们可以直接使用枚举值进行查询、比较和排序操作,而 MySQL 会在内部将其处理为相应的索引值

枚举值的比较

在枚举列中,枚举值的比较是通过比较其对应的整数索引来实现的。每个枚举值都被映射到一个整数值,这个整数值是其在枚举类型中的索引位置。

CREATE TABLE tb (
    status ENUM('active', 'inactive', 'pending')
);

在这个示例中,枚举列 status 包含三个枚举值:activeinactivepending。这些枚举值分别被映射到整数索引 012

当对枚举列进行比较时,实际上是在比较整数索引。例如,以下查询将返回status 列值为 active 的行:

SELECT * FROM tb WHERE status = 'active';

在执行此查询时,MySQL 首先会将字符串 active 与枚举列中的每个枚举值进行比较。因为 active 对应的整数索引是 0,MySQL实际上会执行以下比较操作:

SELECT * FROM tb WHERE status = 0;

MySQL 将比较整数索引 0 和枚举列中每个值的索引,如果匹配则返回相应的行。

通过将枚举值映射到整数索引,比较过程变得更加高效。MySQL 可以直接进行整数比较,而不需要解析和比较字符串的每个字符,这提高了比较操作的性能。

枚举值的排序

枚举值在排序时是基于其对应的整数索引进行排序的。由于枚举值被映射到整数索引,对枚举列进行排序实际上是对整数索引进行排序。

考虑以下 ENUM 列的定义:

CREATE TABLE tb (
	process VARCHAR(15),
    status ENUM('active', 'inactive', 'pending')
);

在这个例子中,枚举值 active 具有索引 1inactive 具有索引2,pending' 具有索引 3。因此,默认情况下,对 status 列进行排序时,active 将排在前面,inactive 在其后,pending 排在最后。

举个栗子

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    process VARCHAR(15),
    status  ENUM ('active', 'inactive', 'pending')
);


INSERT INTO tb
VALUES ('RedHeart', '3'),
       ('TwoMoons', '1'),
       ('BinaryMoon', '2');


SELECT *
FROM tb
ORDER BY status;

执行效果

+------------+----------+
| process    | status   |
+------------+----------+
| TwoMoons   | active   |
| BinaryMoon | inactive |
| RedHeart   | pending  |
+------------+----------+
3 rows in set (0.00 sec)

枚举值不可为表达式

枚举值在MySQL中不能包含表达式。枚举值应该是预定义的常量值,而不是动态计算的表达式(即使这些表达式返回的结果为一个字符串常量)。对此,请参考如下语句:

CREATE TABLE tb
(
    process VARCHAR(15),
    status  ENUM ('active', CONCAT('in', 'active'), 'pending')
);

MySQL 在执行上述语句时将产生异常错误。

约束与枚举列的默认值

若枚举列使用约束 NULL ,且在插入过程中省略对枚举值的插入,则默认将插入 NULL 作为枚举值。若枚举列使用约束 NOT NULL,且在插入过程中省略对枚举值的插入,则默认将插入该枚举类型的索引 1

当然,你可以通过 DEFAULT 约束来制约 NULLNOT NULL 约束所带来的影响。

NULL 约束

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    process VARCHAR(15),
    # 为枚举列添加 NULL 约束
    status  ENUM ('active', 'inactive', 'pending') NULL
);


INSERT INTO tb (process)
VALUES ('RedHeart'),
       ('TwoMoons'),
       ('BinaryMoon');


SELECT *
FROM tb;

查询结果

+------------+--------+
| process    | status |
+------------+--------+
| RedHeart   | NULL   |
| TwoMoons   | NULL   |
| BinaryMoon | NULL   |
+------------+--------+
3 rows in set (0.00 sec)

NOT NULL 约束

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    process VARCHAR(15),
    # 为枚举列添加 NOT NULL 约束
    status  ENUM ('active', 'inactive', 'pending') NOT NULL
);


INSERT INTO tb (process)
VALUES ('RedHeart'),
       ('TwoMoons'),
       ('BinaryMoon');


SELECT *
FROM tb;

查询结果

+------------+--------+
| process    | status |
+------------+--------+
| RedHeart   | active |
| TwoMoons   | active |
| BinaryMoon | active |
+------------+--------+
3 rows in set (0.00 sec)

DEFAULT

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    process VARCHAR(15),
    # 为枚举列添加 DEFAULT 约束
    status  ENUM ('active', 'inactive', 'pending') DEFAULT 'pending'
);


INSERT INTO tb (process)
VALUES ('RedHeart'),
       ('TwoMoons'),
       ('BinaryMoon');


SELECT *
FROM tb;

查询结果

+------------+---------+
| process    | status  |
+------------+---------+
| RedHeart   | pending |
| TwoMoons   | pending |
| BinaryMoon | pending |
+------------+---------+
3 rows in set (0.00 sec)

注:

在使用 DEFAULT 约束为枚举列指定默认值时,默认值必须为有效的枚举值(不可为数值或数值字符串)。否则,MySQL 将为此抛出异常错误。

尾部空格的自动清除

在 MySQL 中,枚举类型的预定义值在存储时会 自动 去除尾部的空格。当将值插入枚举列之前,MySQL 会 清除 该值的尾部空格,并按照一定的规则将其 转换 为对应的索引值后进行 存储

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    process VARCHAR(15),
    # 为每一个枚举值的末尾添加三个半角空格
    status  ENUM ('active   ', 'inactive   ', 'pending   ')
);


INSERT INTO tb
# 为每一个插入的值的尾部添加六个半角空格
VALUES ('RedHeart', 'active      '),
       ('TwoMoons', 'active      '),
       ('BinaryMoon', '3      ');


SELECT process, CONCAT('(', status, ')')
FROM tb;

对于上述代码,有:

  1. 创建表时,在枚举列 status 的每一个预定义值后面都添加了三个半角空格。枚举值在存储于 枚举类型 中前,尾部的空格将会被自动去除。

  2. 在插入数据时,每个插入值的尾部都添加了六个半角空格。即使插入值带有额外的尾部空格,MySQL 也会在插入前将其 清除。因此,向枚举列插入的三个值均能够于枚举类型中存储的值进行匹配。在匹配成功后,MySQL 将会把枚举值对应的索引存储于表中。

  3. 在查询数据时,通过使用 CONCAT 函数将 status 列的值和括号拼接在一起进行显示。这是为了展示查询结果中的枚举值是否存在尾部的空格。

查询结果

+------------+--------------------------+
| process    | CONCAT('(', status, ')') |
+------------+--------------------------+
| RedHeart   | (active)                 |
| TwoMoons   | (active)                 |
| BinaryMoon | (pending)                |
+------------+--------------------------+
3 rows in set (0.00 sec)

严格模式与非严格模式

Strict Mode 与 Non-Strict Mode

在 MySQL 中,有两种模式 用于控制数据处理的严格性,分别是 严格模式(Strict Mode)非严格模式(Non-Strict Mode),它们对于数据处理和校验的方式有所不同。

  1. 严格模式(Strict Mode)

    • 在严格模式下,MySQL 会对数据进行严格的校验和处理,确保数据的 完整性一致性

    • 如果数据不符合定义的数据类型、约束条件或其他规则,MySQL会 抛出错误并终止相应的操作

    • 严格模式可以 确保更高的数据质量,但也要求开发者严格遵守定义的数据规范和限制。

  2. 非严格模式(Non-Strict Mode)

    • 在非严格模式下,MySQL 对数据的校验和处理相对宽松,允许一些不严格的数据转换和处理。

    • 如果数据不符合定义的数据类型或约束条件,MySQL 会 尝试自动进行数据转换或截断,而不会抛出错误

    • 非严格模式下的处理方式 可能会导致数据的丢失或截断,所以开发者 需要更加小心地处理数据

sql-mode

在 MySQ L的最新版本中,默认情况 下严格模式是启用的,但在旧版本中可能默认为非严格模式。开发者可以根据需要在 MySQL 的配置文件或会话级别设置 sql_mode 参数来启用或禁用严格模式。

无效值的处理方式

在 MySQL 中,在 插入值无法与枚举值进行匹配被解析为无效的枚举值索引 时,严格模式和非严格模式下的 MySQL 将会产生不同的反应。

注:

在严格模式和非严格模式下,对于有效的枚举值插入,MySQL 都会将其存储为 对应的枚举值索引。只有在插入的值无法匹配或解析为有效的枚举值索引时,严格模式和非严格模式下的行为有所不同。

严格模式

如果插入的值无法与枚举列的任何预定义值匹配或被解析为无效的枚举值索引,MySQL 将会 抛出异常错误信息,并中止插入操作 以确保数据的完整性和一致性。

非严格模式

如果插入的值无法与枚举列的任何预定义值匹配或被解析为无效的枚举值索引,MySQL 将会在枚举列中存储索引值零。在检索过程中,若从枚举列中检索到的值为索引零,则将其转化为一个空字符串。对此,请参考如下示例:

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    process VARCHAR(15),
    status  ENUM ('active', 'inactive', 'pending')
);

# 关闭 MySQL 的严格模式(简单粗暴)
SET sql_mode = '';

INSERT INTO tb
VALUES ('RedHeart', 666),
       ('TwoMoons', 'RedHeart'),
       ('BinaryMoon', '520');


SELECT process, CONCAT('(', status, ')')
FROM tb;

查询结果

+------------+--------------------------+
| process    | CONCAT('(', status, ')') |
+------------+--------------------------+
| RedHeart   | ()                       |
| TwoMoons   | ()                       |
| BinaryMoon | ()                       |
+------------+--------------------------+
3 rows in set (0.00 sec)
检索枚举列中的无效值

无效值的索引值为 0。这意味着您可以使用以下 SELECT 语句查找被赋予无效枚举值的行:

SELECT * FROM tbl_name WHERE enum_col=0;

举个栗子

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    process VARCHAR(15),
    # 枚举值列表中支持所有字符串,包括空字符串。
    # 枚举值列表中的空字符串将转换为对应的枚举值索引。
    # 而枚举值索引 0 将被转换为空字符串。
    status  ENUM ('active', 'inactive', 'pending', '')
);

# 关闭 MySQL 严格模式(简单粗暴)
SET sql_mode = '';

INSERT INTO tb
VALUES ('RedHeart', 1),
       ('TwoMoons', 'RedHeart'),
       ('BinaryMoon', '');


# 通过枚举类型的默认值机制向枚举列中插入 NULL 值
INSERT INTO tb (process)
VALUES ('ZZH');


SELECT process, CONCAT('(', status, ')')
FROM tb;

# 检索枚举列中的无效值
SELECT process, CONCAT('(', status, ')')
FROM tb WHERE status = 0;

查询结果

# SELECT process, CONCAT('(', status, ')')
# FROM tb;
+------------+--------------------------+
| process    | CONCAT('(', status, ')') |
+------------+--------------------------+
| RedHeart   | (active)                 |
| TwoMoons   | ()                       |
| BinaryMoon | ()                       |
| ZZH        | NULL                     |
+------------+--------------------------+
4 rows in set (0.00 sec)

# SELECT process, CONCAT('(', status, ')')
# FROM tb WHERE status = 0;
+----------+--------------------------+
| process  | CONCAT('(', status, ')') |
+----------+--------------------------+
| TwoMoons | ()                       |
+----------+--------------------------+
1 row in set (0.00 sec)

相同枚举值的处理方式

在创建枚举列的过程中,不要为同一枚举列指定多个相同的枚举值。在 MySQL 的 严格模式 下,MySQL 将为此抛出错误。

枚举值排序的特殊情形

枚举值根据它们的索引号进行排序,索引号取决于在列定义中列出枚举成员的顺序。若对包含 NULL 值的枚举列进行排序,NULL 将会排在所有其他枚举值之前。对此,请参考如下示例:

CREATE DATABASE db_test;

USE db_test;

CREATE TABLE tb
(
    process VARCHAR(15),
    status  ENUM ('active', 'inactive', 'pending', '')
);

SET sql_mode = '';

INSERT INTO tb
VALUES ('RedHeart', 1),
       ('TwoMoons', 'RedHeart'),
       ('BinaryMoon', '');


INSERT INTO tb (process)
VALUES ('ZZH');


SELECT process, CONCAT('(', status, ')')
FROM tb ORDER BY status;

查询结果

+------------+--------------------------+
| process    | CONCAT('(', status, ')') |
+------------+--------------------------+
| ZZH        | NULL                     |
| TwoMoons   | ()                       |
| RedHeart   | (active)                 |
| BinaryMoon | ()                       |
+------------+--------------------------+
4 rows in set (0.00 sec)
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BinaryMoon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值