MySQL 字符集与排序规则问题

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MySQL 字符集与排序规则问题

前言


最近遇到一些问题,业务上在通过查询指定某些公司名称的字段,名称中有全角和半角的括号名称,由于排序规则不同,查询出来的结果也会有不同。所以就些问题做了一些总结,方便以供同样遇到此类问题的同学进行参考。


字符集与排序规则的概念


字符集


字符集是一组抽象的字符(Charcter)组合的集合。举一个例子,所有的汉字就算一个“字符集合”, 所有的英语字母也算一个“字符集合”。 注意,我这里说它们是字符集合,而且还有双引号。是因为字符集并不简单的是字符的集合, 准确概述来说,字符集是一套符号和编码的规则。

字符集需要以某种字符编码方式来表示、存储字符。我们知道,计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit)有0和1两种状态。而如果用不同的0和1组合表示不同的字符就是编码。


字符集与字符编码


字符集是书写系统字母与符号的集合,而字符编码则是将字符映射为一特定的字节或字节序列,是一种规则。通常特定的字符集采用特定的编码方式(例如:ASCII、IOS-8859-1、GB2312、GBK,都是即表示了字符集又表示了对应的字符编码),因此基本上可以将两者视为同义词。


排序规则


是指对指定字符集下不同字符的比较规则。

在MySQL 中,每种字符集都对应多种排序规则,所以可以分别设置字符集和排序规则。而在SQL Server 排序规则和字符集一起设定的。


MySQL 支持字符集


MySQL 支持的字符集查看,以及字符集默认的排序规则。

show character set;


MySQL 支持的排序规则


查看MySQL 查看的排序规则

CS :区分大小写

CI:不区会大小写

AS:区分重音

AI:不区分重音

bin:根据每个位模式进行排序比较,即区分大小也,也区分重音。

SHOW COLLATION;


字符集与排序规则应用的优先级


数据库级别


参数:character_set_server

默认值:utf8mb4

默认数据库使用此字符集。


创建数据库指定字符集

CREATE DATABASE db10 DEFAULT CHARACTER SET latin7 COLLATE latin7_general_cs;


说明:

  1. 不指定排序规则,将使用字符集默认排序规则。
  2. 默认排序规则,在使用show create database 不会被显示。


表级别

CREATETABLE `t1` (  `id` int DEFAULT NULL,  `name` varchar(20)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

说明:

1.不指定字符集和排序规则将继承数据库的。


字段级别

CREATETABLE `t1` (  `id` int DEFAULT NULL,  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL) ENGINE=InnoDB;

优先级

从大到小:字段 > 表 > 数据库


常用的几种字符集


  1. ascii:美国信息交换标准代码,使用的128个字符。占用一个字节。
  2. big5:大五码,使用的是繁体中文。最多占用两个字节。
  3. gb18030:包含gb2312和gbk,支持简体中文,繁体中文和少数民族文字。最多4个字节。
  4. gb2312:支持简体中文,最多2个字节。
  5. gbk:支持简体中文,繁体中文,最多2个字节。
  6. latin1:Latin1是ISO-8859-1的别名,除ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。占用一个字节。
  7. utf8(utf8mb3):是针对Unicode的一种可变长度字符编码。支持 拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文, 中日韩文字、东南亚文字、中东文字等。最多3字节。
  8. utf8mb4:除了包含utf8 以外,还支持 Emoji 表情。最多4字节。


字符集使用不当引起的问题


索引问题


当进行关联查询时,两个相关联的字段,字符集不一样,可能存在隐式转换,或者直接不走索引,从而影响性能。


示例:


创建表

CREATETABLE `t1` (  `id` bigintNOTNULL AUTO_INCREMENT,  `org_id` varchar(60)NOTNULL,  `user_id` varchar(60)NOTNULL,  PRIMARY KEY (`id`),  UNIQUE KEY `idx_org_id_user_id` (`org_id`,`user_id`),  KEY `idx_user_id` (`user_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;CREATETABLE `t2` (  `id` bigintNOTNULL AUTO_INCREMENT,  `c_user_id` varchar(60)NOTNULL,  `type` tinyintNOTNULL,  `user_id` varchar(60)NOTNULL,  PRIMARY KEY (`id`),  UNIQUE KEY `idx_c_user_id_type` (`c_user_id`,`type`),  KEY `idx_user_id_type` (`user_id`,`type`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;


查询

explain SELECT*FROM(SELECT*FROM t1
WHERE `org_id` ='625e8603f70a663212e3148b') o
LEFT JOIN `t2` b
ON o.`user_id` = b.`c_user_id`
AND b.`type` =1;



将字段类型都改成utf8mb4,再看执行计划


全角和半角


全角和半角主要和排序规则有关系


示例:


创建两个表

分别使用utf8mb4_general_ci 和 utf8mb4_0900_ai_ci 排序规则

CREATETABLE `t1` (  `id` intNOTNULL AUTO_INCREMENT,  `name` varchar(20),  PRIMARY KEY(`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 collate=utf8mb4_0900_ai_ci;CREATETABLE `t2` (  `id` intNOTNULL AUTO_INCREMENT,  `name` varchar(20),  PRIMARY KEY(`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 collate=utf8mb4_general_ci;

插入数据

insertinto t1(name)values('北京(某某)公司'),('北京(某某)公司');insertinto t2(name)values('北京(某某)公司'),('北京(某某)公司');

验证排查规则带来的差异

mysql>select*from t1 where name ='北京(某某)公司';+----+--------------------------+| id | name                     |+----+--------------------------+|1| 北京(某某)公司           ||2| 北京(某某)公司         |+----+--------------------------+2 rows inset(0.03 sec)mysql>select*from t2 where name ='北京(某某)公司';+----+----------------------+| id | name                 |+----+----------------------+|1| 北京(某某)公司       |+----+----------------------+1 row inset(0.01 sec)


原因

utf8mb4_0900_ai_ci:支持一下些扩展的功能,所以会兼容一些问题。

utf8mb4_general_ci:仅仅是字符之间一一比较。速度要比utf8mb4_0900_ai_ci快。

所以就会有上面的查询不同的现象。


乱码


示例:


设置一种不支持中文的字符集

set names latin1;

说明

  1. 此命令是会话级别的操作。
  2. 将 character_set_client,character_set_connection,character_set_results三个会话级别的参数都设置成latin1


验证

mysql>select*from t2 where name ='北京(某某)公司';Empty set(0.01 sec)mysql>select*from t2;+------+----------+| id   | name     |+------+----------+|NULL| ??(??)?? ||NULL| ???????? |+------+----------+2 rows inset(0.00 sec)

说明

  1. 此时使用正确的name 已经无法检索到匹配的记录。
  2. 通过列出全表记录,已经成为乱码。
  3. 原因就是配置的字符集不一致辞,导致出现问题。


尾部空格


示例:


插入数据

insertinto t1(name)values('北京(某某)公司 '),('北京(某某)公司 ');insertinto t2(name)values('北京(某某)公司 '),('北京(某某)公司 ');

验证

mysql>select*from t1 where name ='北京(某某)公司 ';+----+---------------------------+| id | name                      |+----+---------------------------+|3| 北京(某某)公司            ||4| 北京(某某)公司          |+----+---------------------------+2 rows inset(0.00 sec)mysql>select*from t2 where name ='北京(某某)公司 ';+----+-----------------------+| id | name                  |+----+-----------------------+|1| 北京(某某)公司        ||3| 北京(某某)公司        |+----+-----------------------+2 rows inset(0.01 sec)


说明


  1. t1的结果说明
  1. 排序规则是 utf8mb4_0900_ai_ci
  2. 是兼容全角和半角
  3. 此排序规则,尾部不会填充空白。
  4. 所以结果是b和c 的组合策略,最终会得到第三和第四条记录。


  1. t2 的结果说明
  1. 排序规则是utf8mb4_general_ci
  2. 不兼容全角和半角,所以只能得到半角记录。
  3. 此排序规则,尾部有填充空白,所以无法区分真实的字符是否有空白。
  4. 最终得到第一和第三条记录。


参考

https://dev.mysql.com/doc/refman/8.0/en/charset.html

https://learn.microsoft.com/zh-cn/sql/relational-databases/collations/collation-and-unicode-support?redirectedfrom=MSDN&view=sql-server-ver16


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4月前
|
自然语言处理 关系型数据库 MySQL
如何在mysql数据库里进行文本的相似度排序?
【8月更文挑战第28天】如何在mysql数据库里进行文本的相似度排序?
389 62
|
1月前
|
SQL NoSQL 关系型数据库
2024Mysql And Redis基础与进阶操作系列(5)作者——LJS[含MySQL DQL基本查询:select;简单、排序、分组、聚合、分组、分页等详解步骤及常见报错问题所对应的解决方法]
MySQL DQL基本查询:select;简单、排序、分组、聚合、分组、分页、INSERT INTO SELECT / FROM查询结合精例等详解步骤及常见报错问题所对应的解决方法
|
2月前
|
SQL 搜索推荐 关系型数据库
MySQL 如何实现 ORDER BY 排序?
本文详细解析了MySQL中`ORDER BY`的实现原理及优化方法。通过解析与优化、执行及多种优化技术,如索引利用、内存排序、外部排序等,帮助你提升排序性能。了解其背后的机制,可显著优化查询效率。
74 4
|
2月前
|
SQL 搜索推荐 关系型数据库
MySQL 如何实现 ORDER BY 排序?
在实际开发中,我们经常会使用 MySQL 的 `ORDER BY`进行排序,那么,`ORDER BY`是如何实现的排序的?我们该如何优化 `ORDER BY`的排序性能?这篇文章,我们来聊一聊。
37 3
|
3月前
|
算法 关系型数据库 MySQL
MySQL高级篇——排序、分组、分页优化
排序优化建议、案例验证、范围查询时索引字段选择、filesort调优、双路排序和单路排序、分组优化、带排序的深分页优化
MySQL高级篇——排序、分组、分页优化
|
3月前
|
自然语言处理 关系型数据库 MySQL
match如何在mysql数据库里进行文本的相似度排序?
【9月更文挑战第1天】match如何在mysql数据库里进行文本的相似度排序?
123 1
|
4月前
|
SQL 关系型数据库 MySQL
MySQL】-DQL(基本、条件、分组、排序、分页)详细版
通过这些查询方法,你可以高效地检索、分析和组织MySQL数据库中的数据,以满足各种应用需求。实践中,理解这些SQL语句的基础知识以及它们如何组合起来进行复杂的数据操作是至关重要的。
52 1
|
4月前
|
算法 关系型数据库 MySQL
揭秘MySQL中的版本号排序:这个超级算法将颠覆你的排序世界!
【8月更文挑战第8天】在软件开发与数据管理中,正确排序版本号对软件更新及数据分析至关重要。因MySQL默认按字符串排序版本号,可能出现'1.20.0'在'1.10.0'之前的不合理情况。解决办法是将版本号各部分转换为整数后排序。例如,使用`SUBSTRING_INDEX`和`CAST`函数从`software`表的`version`字段提取并转换版本号,再按这些整数排序。这种方法可确保版本号按逻辑正确排序,适用于'major.minor.patch'格式的版本号。对于更复杂格式,需调整处理逻辑。掌握此技巧可有效应对版本号排序需求。
204 3
|
5月前
|
存储 人工智能 关系型数据库
MySQL 8.0 字符集与比较规则介绍
我们都知道 MySQL 8.0 与 MySQL 5.7 的区别之一就是默认字符集从 latin1 改成了 utf8mb4 ,除此之外,MySQL 8.0 下的字符集和比较规则还有没有其他变化呢?本篇文章我们一起来学习下。
306 1
|
5月前
|
关系型数据库 MySQL 数据库
MySQL设计规约问题之索引的命名规则是什么
MySQL设计规约问题之索引的命名规则是什么