暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL的字符集中校验规则

程序猿读历史 2021-05-31
2035

一、字符集和校验规则

字符集是一套符号和编码,校验规则(collation)是在字符集内用于比较字符的一套规则,即字符集的排序规则。

MySQL服务器可以支持多种字符集,在同一台服务器,同一个数据库,甚至同一个表的不同字段都可以指定使用不同的字符集,相比oracle等其他数据库管理系统,在同一个数据库只能使用相同的字符集,MySQL明显存在更大的灵活性,但是灵活并不是一件好事情。

每种字符集都可能有多种校对规则,并且都有一个默认的校对规则,并且每个校对规则只是针对某个字符集,和其他的字符集没有关系。



首先创建一个表,ddl语句如下:


    CREATE TABLE `t_test_20191029_ci` (
    `id` char(25) NOT NULL ,
    `name_id` varchar(20) DEFAULT NULL,
    `name` text DEFAULT NULL,
    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;


    CREATE TABLE `t_test_20191029_bin` (
    `id` char(25) NOT NULL ,
    `name_id` varchar(20) DEFAULT NULL,
    `name` text DEFAULT NULL,
    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;


    上面建表语句相信大部分同学都能懂,但是对COLLATE=utf8mb4_general_ci或许有点懵逼,大部分同学看到这都会骂娘:这tm到底是什么sb东西,字符集难道不是前面CHARSET=utf8mb4,算了不管了,反正不影响我撸代码。



    我们的故事从骂娘开始。


    1.1 COLLATE作用


    其实COLLATE是字符集中用于排序的一个校验规则。在mysql中,字符类型的列,比如:char、text、varchar等类型的列,都需要有collate和mysql进行交互,告诉mysql这些列该如何进行排序。因此,COLLATE和orderby、distinct、group by、having语句息息相关,同时也会影响where条件中大于小于等于的查询


    另外,如果mysql创建索引的列是字符类型,也会影响该索引创建,因为mysql索引实质有序的排列的数据结构,所以只要跟字符类型比较或排序的地方,都会和COLLATE属性有联系。



    1.2 COLLATE的区别


    COLLATE通常是和数据字符集(CHARSET)相关的,一般来说每种CHARSET都有多种它所支持的COLLATE,并且每种CHARSET都指定一种COLLATE为默认值。例如Latin1编码的默认COLLATE为latin1_swedish_ci,GBK编码的默认COLLATE为gbk_chinese_ci,utf8mb4编码的默认值为utf8mb4_general_ci。


    COLLATE都带有_ci,这是Case Insensitive单词的缩写,就是和大小写无关,"A"和"a"在排序和比较的时候是一视同仁的。selection * from test where cloumn="a"同样可以把cloumn为"A"的值选出来。以_cs后缀的COLLATE,则是Case Sensitive,即大小写敏感的。



    到此我们对最开始创建的两个表做一个小小的测试,dml语句如下:


      insert into t_test_20191029_ci (id) values('A');
      insert into t_test_20191029_ci (id) values('a');
      insert into t_test_20191029_ci (id) values('Aa');


      insert into t_test_20191029_bin (id) values('A');
      insert into t_test_20191029_bin (id) values('a');
      insert into t_test_20191029_bin (id) values('Aa');


      然后我们分别查询下id='A':



        select * from t_test_20191029_ci where id='A';
        +----+---------+------+---------------------+
        | id | name_id | name | create_time |
        +----+---------+------+---------------------+
        | A | NULL | NULL | 2019-10-30 16:39:42 |
        | a | NULL | NULL | 2019-10-30 16:39:43 |
        +----+---------+------+---------------------+
        2 rows in set (0.01 sec)


        select * from t_test_20191029_bin where id='A';
        +----+---------+------+---------------------+
        | id | name_id | name | create_time |
        +----+---------+------+---------------------+
        | A | NULL | NULL | 2019-10-30 16:43:16 |
        +----+---------+------+---------------------+
        1 row in set (0.00 sec)

        实验结果发现,当校验规则是:utf8mb4_general_ci,该列是不区分大小写的,所以where id='A',会出现两行。省略order by、 having 、group by、distinct的实验,原理是和where一样的。



        1.3 类型


        mysql中执行show collation命令,可以看到所有字符集的校验规则,因为通常的字符集都是utf8mb4,所以只看该模式下的校验规则。



          show collation where Charset ='utf8mb4';
          +------------------------+---------+-----+---------+----------+---------+
          | Collation | Charset | Id | Default | Compiled | Sortlen |
          +------------------------+---------+-----+---------+----------+---------+
          | utf8mb4_general_ci | utf8mb4 | 45 | Yes | Yes | 1 |
          | utf8mb4_bin | utf8mb4 | 46 | | Yes | 1 |
          | utf8mb4_unicode_ci | utf8mb4 | 224 | | Yes | 8 |
          | utf8mb4_icelandic_ci | utf8mb4 | 225 | | Yes | 8 |
          | utf8mb4_latvian_ci | utf8mb4 | 226 | | Yes | 8 |
          | utf8mb4_romanian_ci | utf8mb4 | 227 | | Yes | 8 |
          | utf8mb4_slovenian_ci | utf8mb4 | 228 | | Yes | 8 |
          | utf8mb4_polish_ci | utf8mb4 | 229 | | Yes | 8 |
          | utf8mb4_estonian_ci | utf8mb4 | 230 | | Yes | 8 |
          | utf8mb4_spanish_ci | utf8mb4 | 231 | | Yes | 8 |
          | utf8mb4_swedish_ci | utf8mb4 | 232 | | Yes | 8 |
          | utf8mb4_turkish_ci | utf8mb4 | 233 | | Yes | 8 |
          | utf8mb4_czech_ci | utf8mb4 | 234 | | Yes | 8 |
          | utf8mb4_danish_ci | utf8mb4 | 235 | | Yes | 8 |
          | utf8mb4_lithuanian_ci | utf8mb4 | 236 | | Yes | 8 |
          | utf8mb4_slovak_ci | utf8mb4 | 237 | | Yes | 8 |
          | utf8mb4_spanish2_ci | utf8mb4 | 238 | | Yes | 8 |
          | utf8mb4_roman_ci | utf8mb4 | 239 | | Yes | 8 |
          | utf8mb4_persian_ci | utf8mb4 | 240 | | Yes | 8 |
          | utf8mb4_esperanto_ci | utf8mb4 | 241 | | Yes | 8 |
          | utf8mb4_hungarian_ci | utf8mb4 | 242 | | Yes | 8 |
          | utf8mb4_sinhala_ci | utf8mb4 | 243 | | Yes | 8 |
          | utf8mb4_german2_ci | utf8mb4 | 244 | | Yes | 8 |
          | utf8mb4_croatian_ci | utf8mb4 | 245 | | Yes | 8 |
          | utf8mb4_unicode_520_ci | utf8mb4 | 246 | | Yes | 8 |
          | utf8mb4_vietnamese_ci | utf8mb4 | 247 | | Yes | 8 |
          +------------------------+---------+-----+---------+----------+---------+
          26 rows in set (0.00 sec)


          可以看到utf8mb4字符集下,默认校验规则是:utf8mb4_general_ci。


          我们常用的校验规则是utf8mb4_general_ci、utf8mb4_unicode_ci、utf8mb4_bin这三个。我们来探究一下这三个的区别:


          首先utf8mb4_bin的比较方法其实就是直接将所有字符看作二进制串,然后从最高位往最低位比对。所以很显然它是区分大小写的。



          而utf8mb4_unicode_ci和utf8mb4_general_ci对于中文和英文来说,其实是没有任何区别的,但是对于英语之外的其他语言会有一定影响。官方文档使用utf8mb4_unicode_ci,但是对于使用了默认值的系统,也并没有非常排斥,并不认为有什么大问题。对于已经用了utf8mb4_general_ci的系统,也没有必要花时间改造。


          另外从mysql 8.0开始,mysql默认的CHARSET已经不再是Latin1了,改为了utf8mb4,并且默认的COLLATE也改为了utf8mb4_0900_ai_ci。utf8mb4_0900_ai_ci就是unicode的进一步细分。



          到此我们总结COLLATE到底是什么及其作用:

          1、COLLATE是在字符集内用于比较字符的一套规则。、

          2、COLLATE只对字符串类型列有效果,比如:varchar、char、text等,对时间类型和数字类型无影响。

          3、COLLATE主要有区分大小写(utf8mb4_bin)和不区分大小写(utf8mb4_general_ci)两种。


          基于上两点,可以知道以下两点:

          1、不同的COLLATE对order by、 having 、group by,以及where 条件的中的大于小于等于有影响。

          2、不同的COLLATE下会影响mysql的索引使用。


          文章转载自程序猿读历史,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论