MySQL 是否大小写敏感

本文未做特别说明的,指同时适用于 MySQL 5.7 和 MySQL 8.x。

先说结论:

  • 数据库、表名(包括别名)、触发器名是否大小写敏感与操作系统以及 MySQL 配置有关。
  • 列名(包括别名)、索引、存储过程和事件名称大小写不敏感。
  • 字段内容大小写是否敏感与字符集排序规则有关。

MySQL 大小写规则

在 MySQL 中,数据库对应于数据目录中的目录。数据库中的每个表对应于数据库目录中的至少一个文件(也可能更多,具体取决于存储引擎)。触发器也对应于文件。因此,底层操作系统的大小写敏感度会影响数据库、表和触发器名称的大小写敏感度。这意味着此类名称在 Windows 中不区分大小写,但在大多数 Unix 版本中区分大小写。一个值得注意的例外是 macOS,它基于 Unix,但使用不区分大小写的默认文件系统类型 (HFS+)。

列、索引、存储过程和事件名称在任何平台上都不区分大小写,列别名也不区分大小写。

lower_case_table_names 系统变量

表和数据库名称如何存储在磁盘上以及如何在 MySQL 中使用受到 lower_case_table_names 系统变量的影响,但该变量不影响触发器标识符的大小写敏感性。

lower_case_table_names 在 Unix 上,默认值为 0。在 Windows 上,默认值为 1。在 macOS 上,默认值为 2。

含义
0 表和数据库名称使用以大小写敏感的形式存储在磁盘上,名称比较(比如查询)时区分大小写。如果在文件名不区分大小写的系统(例如 Windows 或 macOS)上运行 MySQL,则不应将此变量设置为 0 。如果在不区分大小写的文件系统上强制将变量 lower_case_table_names 设置为 0,并在基于 MyISAM 数据引擎的表使用不同的字母大小写访问表名,则可能会导致索引损坏。
1 表名称以小写形式存储在磁盘上,名称比较不区分大小写。MySQL 在存储和查找时将所有表名转换为小写。此行为也适用于数据库名称和表别名。
2 表和数据库名称使用以大小写敏感的形式存储在磁盘上,但 MySQL 在查找时将它们转换为小写。名称比较不区分大小写。这仅适用于不区分大小写的文件系统!基于 InnoDB 数据引擎的表名和视图名以小写形式存储,正如 lower_case_table_names = 1 的情况。

另外需要注意的一点是,在 MySQL 8 中 lower_case_table_names 变量只能在MySQL服务器初始化时配置,初始化后不允许修改。所以,如何配置该参数要提前规划好,防止后续修改时需要数据迁移,导致其他问题。

MySQL 的字符集与排序规则

通俗讲字符集就是字符码的集合,在 MySQL 中字符集的选择影响字符码的存储,字符集选择不好不仅影响存储,连展示都会有问题,例如乱码。

在业务中常用的字符集是 UTF-8 字符集,MySQL 有两种这样的字符集:utf8、utf8mb4,它们的区别如下:

  • utf8: 支持最长 3 Byte 的字符编码,但一部分 UTF-8 的 4 Byte 编码不支持,例如 emoji。
  • utf8mb4:支持最长 4 Byte 的字符编码。这是业务中使用最多的字符集,从 MySQL 8.0 开始成为默认字符集。

而字符的排序规则决定了字符在比较、排序时以及大小写敏感的规则。涉及字符比较的操作均与其相关,例如:排序、分组、索引、比较(=、>、<等)。

在 MySQL 中字符集的排序规则,决定了字段是否大小写敏感。这些排序规则一般以相关的字符集名开始,通常包括一个语言名,并且以 _ci_cs_bin 结束 。

  • ci 为 case insensitive 的缩写,即大小写不敏感。
  • cs 为 case sensitive的缩写,即大小写敏感。
  • bin 将字符串中的每一个字符用二进制数据存储,区分大小写。

举例来说,当使用 utf8mb4 字符集,默认的字段排序规则是 utf8mb4_general_ci,即字符大小写不敏感。

-- 所有字段都是 utf8mb4 字符集,且均使用 utf8mb4_general_ci 排序规则
create table user(
	id int(11) not null auto_increment,
	username varchar(127) default null,
	nickname varchar(127) default null,
	primary key (id),
	unique key uniq_idx_username (username)
) engine=innodb default charset=utf8mb4;

我们可以插入几条数据进行测试:

INSERT INTO `user` (`username`) VALUES ('user');
INSERT INTO `user` (`username`) VALUES ('User');
INSERT INTO `user` (`username`) VALUES ('USER');

使用查询语句查询 username 为全部小写的 user 的用户,结果三条记录全部都查询到了,这是因为此时所有字段是大小写不敏感的。

mysql> SELECT username from user where username = 'user';
+----------+
| username |
+----------+
| user     |
| User     |
| USER     |
+----------+
3 rows in set

那么,我想要只搜索出 username 为 user 的用户该怎么做呢?

解决方案就是使用 BINARY 关键字来指定字符字段的排序规则:

mysql> select * from user where BINARY username ='user';
+----+----------+
| id | username |
+----+----------+
|  1 | user     |
+----+----------+
1 row in set

这种方式相对较简单,不用改动表结构,只需在需要区分查询的字段前加上关键字。但这种方式也是有缺点的,每次写查询的时候都要注意加关键字,而且可能需要改动较多的代码。

另一种方式是在建表时就对 username 字段进行限制:

-- 均使用 utf8mb4_general_ci 排序规则
CREATE TABLE USER (
	id INT (11) NOT NULL auto_increment,
	username VARCHAR (127) BINARY NOT NULL,
	nickname VARCHAR (127) DEFAULT NULL,
	PRIMARY KEY (id),
	UNIQUE KEY uniq_idx_username (username) 
) ENGINE = INNODB DEFAULT charset = utf8mb4;

如果需要整个表都是大小写敏感的,可以在创建表时使用 utf8mb4_bin 排序规则。

create table user(
	id int(11) not null auto_increment,
	username varchar(127) default null,
	nickname varchar(127) default null,
	primary key (id),
	unique key uniq_idx_username (username)
) engine=innodb default charset=utf8mb4 COLLATE=utf8mb4_bin;
-- 指定 COLLATE=utf8mb4_bin 即可

一些想法

如何设置 lower_case_table_names 参数?

对于 lower_case_table_names 参数设置为 0 可能会带来哪些问题呢?

比如说:一位同事创建了 Test 表,另一位同事在写程序调用时写成了 test 表,则会报错不存在,更甚者可能会出现 TestDb 库与 testdb 库共存,Test 表与 test 表共存的情况,这样就更加混乱了。所以为了实现最大的可移植性和易用性,我们可以采用一致的约定,例如始终使用小写名称创建和引用库表。也可以将 lower_case_table_names 设为 1 来解决此问题。

另外,上面提到了使用小写的表名,这也是阿里的 Java 开发手册推荐的用法。但如果我们还要考虑其他数据库的兼容,比如 Oracle 呢?这时,保持小写表名就不是一个好想法了。

因为,在 Oracle 中,如果表名和字段名在定义的时候是小写的,那么 SQL 操作时候,表名和字段名是需要用引号括起来的。

参考

  • MySQL 5.7 官方文档
  • MySQL 8.2 官方文档
  • MySQL 的字符集与排序规则
  • 关于 MySQL 库表名大小写问题
  • MySQL 大小写参数 lower_case_table_names 学习
  • MySQL 转 Oracle 遇到的问题:表名长度及大小写问题

你可能感兴趣的:(数据库,mysql,数据库)