存储之道:MySQL 中的字符串数据类型之 ENUM

news/2024/11/25 22:00:24/

文章目录

  • 参考
  • 描述
  • 枚举类型
      • 枚举类型
      • 创建枚举类型
      • 优劣
          • 优点
          • 缺点
  • 枚举值与其索引
      • 映射
      • 检索索引
      • 插入的数值与数值字符串都将被解释为索引
      • 由枚举值索引带来的优势
      • 枚举值的比较
      • 枚举值的排序
  • 枚举值不可为表达式
  • 约束与枚举列的默认值
      • NULL 约束
      • NOT NULL 约束
      • DEFAULT
  • 尾部空格的自动清除
  • 严格模式与非严格模式
      • Strict Mode 与 Non-Strict Mode
      • sql-mode
      • 无效值的处理方式
          • 严格模式
          • 非严格模式
          • 检索枚举列中的无效值
      • 相同枚举值的处理方式
  • 枚举值排序的特殊情形

参考

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

描述

项目描述
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 和 femalesex  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)

http://www.ppmy.cn/news/75027.html

相关文章

【BIO、NIO、AIO、Netty】

什么是IO Java中I/O是以流为基础进行数据的输入输出的,所有数据被串行化(所谓串行化就是数据要按顺序进行输入输出)写入输出流。简单来说就是java通过io流方式和外部设备进行交互。在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广…

mysql explain select * from table结果分析

EXPLAIN是MySQL提供的一个查询优化工具,可以用来分析查询语句的执行计划,帮助我们找出查询语句中存在的性能问题。EXPLAIN的语法如下: EXPLAIN SELECT * FROM table;执行这个语句后,MySQL会返回一个结果集,其中包含了…

Spring Gateway使用JWT实现统一身份认证

在开发集群式或分布式服务时,鉴权是最重要的一步,为了方便对请求统一鉴权,一般都是会放在网关中进行处理。目前非常流行的一种方案是使用JWT,详细的使用说明,可以找相关的资料查阅,这里先不进行深入的引用了…

github搜索方法

GitHub可以使用搜索功能来查找存储库、代码、问题、提交记录、分支等。 1.进入GitHub主页(https://github.com/) 2.在搜索框中输入您要搜索的关键字或短语,并按“Enter”键。 3.您将被重定向到一个新页面,其中包含有关您的搜索结果…

【测试开发】 测试题整理01

求一切顺利~ 文章目录 1. 测试与调试的区别2. 软件测试的概念3. 软件测试与软件开发的区别4. 测试人员需要具备的素质5. 软件开发的生命周期6. 为什么要进行测试7. 需求与bug8. 软件缺陷的级别 1. 测试与调试的区别 调试(Debugging)和测试(Test)是软件开发过程中非常重要的两个…

信道通信基础 - 传输介质(双绞线、光纤)

文章目录 1 概述2 传输介质2.1 双绞线2.2 光纤 3 扩展3.1 网工软考真题 1 概述 2 传输介质 2.1 双绞线 双绞线:8 根铜导线每 2 根扭在一起(百兆用 4 根,千兆必须用 8 根)分类 2.2 光纤 光纤:利用光在 玻璃或塑料纤…

机器学习神经网络——Adaboost分离器算法

系列文章目录 机器学习之SVM分类器介绍——核函数、SVM分类器的使用 机器学习的一些常见算法介绍【线性回归,岭回归,套索回归,弹性网络】 机器学习相关概念思维导图 文章目录 系列文章目录 前言 Adaboost算法的简单介绍 Adaboost算法相…