【问题标题】:Troubleshooting "Illegal mix of collations" error in mysql解决 mysql 中的“非法混合排序规则”错误
【发布时间】:2011-03-03 00:59:05
【问题描述】:

尝试通过 MySQL 中的存储过程进行选择时出现以下错误。

用于操作“=”的排序规则 (latin1_general_cs,IMPLICIT) 和 (latin1_general_ci,IMPLICIT) 的非法混合

知道这里可能出了什么问题吗?

表的排序规则是latin1_general_ci,where子句中列的排序规则是latin1_general_cs

【问题讨论】:

  • 你可以试试this script,它将你所有的数据库和表都转换为utf8。
  • 我在很长一段时间内(自 1990 年以来)一直在使用各种数据库,而 NySQL 对排序和强制的使用显得“疯狂”,数据库解决了强加“ONE”字符集的问题对于数据库,则由导入/导出过程来转换/转换为数据库使用的唯一字符集。 Mysql 选择的解决方案是一种破坏性的解决方案,因为将“应用程序问题”(字符集转换)与数据库问题(排序规则使用)混合在一起。为什么不从数据库中“删除”那些愚蠢而繁琐的功能,使其变得更易于使用和控制

标签: mysql collation


【解决方案1】:

在我的例子中,函数的默认返回类型是来自数据库 (utf8mb4_general_ci) 的类型/排序规则,但数据库列是 ascii。

WHERE ascii_col = md5(concat_ws(',', a,b,c))

快速修复是

WHERE ascii_col = BINARY md5(concat_ws(',', a,b,c))

【讨论】:

    【解决方案2】:

    我个人在程序中遇到了这个问题。 如果您不想alter table 您可以尝试将您的参数转换为过程。 我已经尝试过多次使用 collat​​e(在选择中设置一个集合),但没有一个对我有用。

    CONVERT(my_param USING utf32) 成功了。

    【讨论】:

      【解决方案3】:

      以下解决方案对我有用。

      CONVERT( Table1.FromColumn USING utf8)    =  CONVERT(Table2.ToColumn USING utf8) 
      

      【讨论】:

        【解决方案4】:

        将我的 2c 添加到未来 Google 员工的讨论中。

        我正在调查一个类似的问题,在使用接收 varchar 参数的自定义 函数 时出现以下错误:

        Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and 
        (utf8_general_ci,IMPLICIT) for operation '='
        

        使用以下查询:

        mysql> show variables like "collation_database";
            +--------------------+-----------------+
            | Variable_name      | Value           |
            +--------------------+-----------------+
            | collation_database | utf8_general_ci |
            +--------------------+-----------------+
        

        我知道数据库使用的是 utf8_general_ci,而表是使用 utf8_unicode_ci 定义的:

        mysql> show table status;
            +--------------+-----------------+
            | Name         | Collation       |
            +--------------+-----------------+
            | my_view      | NULL            |
            | my_table     | utf8_unicode_ci |
            ...
        

        注意视图有 NULL 排序规则。即使此查询显示一个视图为空,视图和函数似乎也具有排序规则定义。使用的排序规则是创建视图/函数时定义的 DB 排序规则。

        可悲的解决方案是更改数据库排序规则并重新创建视图/函数以强制它们使用当前排序规则。

        • 更改数据库的排序规则:

          ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;
          
        • 更改表格排序规则:

          ALTER TABLE mydb CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
          

        我希望这会对某人有所帮助。

        【讨论】:

        • 排序规则也可以在列级别设置。您可以通过以下方式查看:show full columns from my_table;
        • 谢谢。我刚刚删除了架构,并使用正确的默认排序规则重新创建了它,然后重新导入了所有内容。
        • @JonathanTran 谢谢!我在所有表、数据库和连接上都设置了字符集和排序规则,但它仍然报错!排序规则未设置在列上!我用alter table <TABLE> modify column <COL> varchar(255) collate utf8_general_ci;修复了它
        • 未来谷歌员工的旁注:即使您的数据库、表和字段都具有相同的排序规则,您也必须确保您的连接使用相同的排序规则。一切都有 »utf8mb4_unicode_ci« 但SHOW session variables like '%collation%'; 告诉您 »collat​​ion_connection« 是 »utf8mb4_general_ci«?然后预先运行SET collation_connection = utf8mb4_unicode_ci
        • 谢谢!我花了一段时间才找到这个。不仅表必须是相同的排序规则,数据库也必须如此!
        【解决方案5】:

        非常有趣……现在,准备好。我查看了所有“添加整理”解决方案,对我来说,这些都是创可贴修复。现实情况是数据库设计“糟糕”。是的,标准的变化和新的东西被添加了,等等,但它并没有改变糟糕的数据库设计事实。我拒绝采用在 SQL 语句中添加“整理”只是为了让我的查询正常工作的路线。唯一对我有用并且将来几乎不需要调整代码的解决方案是重新设计数据库/表以匹配我将长期使用和接受的字符集。在这种情况下,我选择使用字符集“utf8mb4”。

        因此,当您遇到“非法”错误消息时,这里的解决方案是重新设计您的数据库和表。它比听起来更容易和更快。甚至可能不需要导出数据并从 CSV 重新导入。更改数据库的字符集并确保表的所有字符集都匹配。

        使用这些命令来指导您:

        SHOW VARIABLES LIKE "collation_database";
        SHOW TABLE STATUS;
        

        现在,如果您喜欢在这里和那里添加“整理”并通过强制“覆盖”来增强您的代码,我猜。

        【讨论】:

          【解决方案6】:

          如果你安装了phpMyAdmin,你可以按照以下链接中的说明进行操作:https://mediatemple.net/community/products/dv/204403914/default-mysql-character-set-and-collation你必须将数据库的排序规则与所有表的排序规则以及表的字段匹配,然后重新编译所有存储过程和函数。有了它,一切都应该重新开始了。

          【讨论】:

            【解决方案7】:

            这通常是由于比较两个不兼容的排序规则的字符串或尝试将不同排序规则的数据选择到一个组合列中引起的。

            子句COLLATE 允许您指定查询中使用的排序规则。

            例如,下面的WHERE 子句总是会给出你发布的错误:

            WHERE 'A' COLLATE latin1_general_ci = 'A' COLLATE latin1_general_cs
            

            您的解决方案是为查询中的两列指定一个共享排序规则。下面是一个使用COLLATE 子句的示例:

            SELECT * FROM table ORDER BY key COLLATE latin1_general_ci;
            

            另一种选择是使用BINARY 运算符:

            BINARY str 是 CAST(str AS BINARY) 的简写。

            您的解决方案可能如下所示:

            SELECT * FROM table WHERE BINARY a = BINARY b;
            

            或者,

            SELECT * FROM table ORDER BY BINARY a;
            

            【讨论】:

            • 谢谢。实际上,在我的情况下,它的行为似乎很奇怪。当我通过查询浏览器按原样运行查询时,它会获取结果。但是使用存储过程会引发错误。
            • 二进制对我来说似乎是最好的解决方案。如果您不使用任何棘手的过滤器,它也可能是最适合您的。
            • 我有同样的问题,我解决这个问题的方法是从头开始重新创建。我尝试更改排序规则,但是当我加入时仍然出现错误,所以我尝试了这种方式。 cmiiw
            • 请注意,使用 COLLATE latin1_general_ci 的 MariaDB 中存在一个错误,这会导致另一个错误:COLLATION 'utf8_general_ci' is not valid for CHARACTER SET 'latin1'' - 即使您没有包含 CHARACTER SET 'latin1' 的列!解决方案是使用 BINARY 演员表。另见this question
            【解决方案8】:

            此代码需要放在 Run SQL query/queries on database

            SQL QUERY WINDOW

            ALTER TABLE `table_name` CHANGE `column_name` `column_name`   VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL;
            

            请将 table_name 和 column_name 替换为适当的名称。

            【讨论】:

              【解决方案9】:

              我遇到了类似的问题,正在尝试将 FIND_IN_SET 过程与字符串 variable 一起使用。

              SET @my_var = 'string1,string2';
              SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);
              

              并且收到错误

              错误代码:1267。非法混合排序规则 (utf8_unicode_ci,IMPLICIT) 和 (utf8_general_ci,IMPLICIT) 用于操作“find_in_set”

              简答:

              无需更改任何 collat​​ion_YYYY 变量,只需在变量声明旁边添加正确的排序规则,即

              SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
              SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);
              

              长答案:

              我首先检查了排序规则变量:

              mysql> SHOW VARIABLES LIKE 'collation%';
                  +----------------------+-----------------+
                  | Variable_name        | Value           |
                  +----------------------+-----------------+
                  | collation_connection | utf8_general_ci |
                  +----------------------+-----------------+
                  | collation_database   | utf8_general_ci |
                  +----------------------+-----------------+
                  | collation_server     | utf8_general_ci |
                  +----------------------+-----------------+
              

              然后我检查了表格排序规则:

              mysql> SHOW CREATE TABLE my_table;
              
              CREATE TABLE `my_table` (
                `id` int(11) NOT NULL AUTO_INCREMENT,
                `column_name` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
                PRIMARY KEY (`id`)
              ) ENGINE=MyISAM AUTO_INCREMENT=125 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
              

              这意味着我的变量配置为默认排序规则 utf8_general_ci 而我的表配置为 utf8_unicode_ci

              通过在变量声明旁边添加 COLLATE 命令,变量排序规则与为表配置的排序规则匹配。

              【讨论】:

                【解决方案10】:

                排序规则问题的另一个来源是mysql.proc 表。检查您的存储过程和功能的排序规则:

                SELECT
                  p.db, p.db_collation, p.type, COUNT(*) cnt
                FROM mysql.proc p
                GROUP BY p.db, p.db_collation, p.type;
                

                还要注意mysql.proc.collation_connectionmysql.proc.character_set_client 列。

                【讨论】:

                  【解决方案11】:

                  如果您遇到问题的列是“哈希”,那么请考虑以下...

                  如果“hash”是一个二进制字符串,你真的应该使用BINARY(...)数据类型。

                  如果“hash”是十六进制字符串,则不需要utf8,由于字符检查等原因应该避免这种情况。例如,MySQL的MD5(...)产生一个固定长度的32字节十六进制字符串。 SHA1(...) 给出一个 40 字节的十六进制字符串。这可以存储到CHAR(32) CHARACTER SET ascii(或 40 用于 sha1)。

                  或者,更好的是,将UNHEX(MD5(...)) 存储到BINARY(16)。这将列的大小减少了一半。 (但是,它确实使它变得相当不可打印。)SELECT HEX(hash) ... 如果您希望它可读。

                  比较两个BINARY 列没有排序问题。

                  【讨论】:

                    【解决方案12】:

                    如果涉及文字的解决方案。

                    我正在使用 Pentaho 数据集成,但没有指定 sql 语法。 使用非常简单的数据库查找给出了错误 "用于操作 '=' 的排序规则 (cp850_general_ci,COERCIBLE) 和 (latin1_swedish_ci,COERCIBLE) 的非法混合”

                    生成的代码是 "SELECT DATA_DATE AS latest_DATA_DATE FROM hr_cc_normalised_data_date_v WHERE PSEUDO_KEY = ?"

                    简而言之,查找是一个视图,当我发布时

                    mysql> show full columns from hr_cc_normalised_data_date_v;
                    +------------+------------+-------------------+------+-----+
                    | Field      | Type       | Collation         | Null | Key |
                    +------------+------------+-------------------+------+-----+
                    | PSEUDO_KEY | varchar(1) | cp850_general_ci  | NO   |     |
                    | DATA_DATE  | varchar(8) | latin1_general_cs | YES  |     |
                    +------------+------------+-------------------+------+-----+
                    

                    这解释了“cp850_general_ci”的来源。

                    视图只是用 'SELECT 'X',......' 创建的 根据这样的手册文字应该从正确定义为“latin1”和“latin1_general_cs”的服务器设置继承它们的字符集和排序规则 因为这显然没有发生,所以我在创建视图时强迫它

                    CREATE OR REPLACE VIEW hr_cc_normalised_data_date_v AS
                    SELECT convert('X' using latin1) COLLATE latin1_general_cs        AS PSEUDO_KEY
                        ,  DATA_DATE
                    FROM HR_COSTCENTRE_NORMALISED_mV
                    LIMIT 1;
                    

                    现在它显示两个列的 latin1_general_cs 并且错误已经消失。 :)

                    【讨论】:

                      【解决方案13】:

                      有时转换字符集可能很危险,尤其是在具有大量数据的数据库上。我认为最好的选择是使用“二进制”运算符:

                      e.g : WHERE binary table1.column1 = binary table2.column1
                      

                      【讨论】:

                      • 这样安全吗?我不知道数据是如何在字符串和二进制之间转换的,但是不同编码的两个不同字符串可以有相同的二进制表示。
                      【解决方案14】:

                      一个可能的解决方案是convert the entire database to UTF8(另见question)。

                      【讨论】:

                        【解决方案15】:

                        TL;DR

                        要么更改一个(或两个)字符串的排序规则以使它们匹配,要么将COLLATE 子句添加到您的表达式中。


                        1. 到底这个“整理”是什么东西?

                          Character Sets and Collations in General 中所述:

                          字符集是一组符号和编码。 collat​​ion 是一组用于比较字符集中的字符的规则。让我们通过一个虚构字符集的示例来明确区分。

                          假设我们有一个包含四个字母的字母表:“A”、“B”、“a”、“b”。我们给每个字母一个数字:“A”=0,“B”=1,“a”=2,“b”=3。字母“A”是一个符号,数字 0 是“A”的编码,所有四个字母及其编码的组合是一个字符集

                          假设我们要比较两个字符串值,“A”和“B”。最简单的方法是查看编码:0 表示“A”,1 表示“B”。因为 0 小于 1,所以我们说“A”小于“B”。我们刚刚所做的是将排序规则应用于我们的字符集。排序规则是一组规则(在这种情况下只有一个规则):“比较编码”。我们将所有可能的排序规则中最简单的称为 binary 排序规则。

                          但是如果我们想说小写字母和大写字母是等价的呢?那么我们至少有两条规则:(1)将小写字母“a”和“b”视为等效于“A”和“B”; (2) 然后比较编码。我们称之为不区分大小写排序规则。它比二进制排序规则复杂一点。

                          在现实生活中,大多数字符集都有许多字符:不仅仅是“A”和“B”,而是整个字母表,有时是多个字母表或具有数千个字符的东方书写系统,以及许多特殊符号和标点符号分数。同样在现实生活中,大多数排序规则都有很多规则,不仅是是否区分字母大小写,还有是否区分重音(“重音”是附加在字符上的标记,如德语“Ö”),以及多字符映射(例如“Ö” = “OE” 在两个德语排序规则之一中的规则)。

                          Examples of the Effect of Collation 下给出了更多示例。

                        2. 好的,但是 MySQL 如何决定对给定表达式使用哪种排序规则?

                          Collation of Expressions 中所述:

                          在绝大多数语句中,很明显 MySQL 使用什么排序规则来解决比较操作。比如以下情况,应该明确排序规则是charset_name列的排序规则:

                          SELECT x FROM T ORDER BY x;
                          SELECT x FROM T WHERE x = x;
                          SELECT DISTINCT x FROM T;
                          

                          但是,对于多个操作数,可能会产生歧义。例如:

                          SELECT x FROM T WHERE x = 'Y';
                          

                          比较应该使用列 x 的排序规则,还是字符串文字 'Y' 的排序规则? x'Y' 都有排序规则,那么哪个排序规则优先?

                          标准 SQL 使用过去称为“强制”的规则来解决此类问题。

                          [ deletia ]

                          MySQL 使用具有以下规则的强制值来解决歧义:

                          • 使用具有最低强制力值的排序规则。

                          • 如果双方的强制力相同,那么:

                            • 如果两边都是Unicode,或者两边都不是Unicode,那就是错误。

                            • 如果一侧具有 Unicode 字符集,而另一侧具有非 Unicode 字符集,则具有 Unicode 字符集的一侧获胜,并且自动字符集转换应用于非 Unicode 一侧。例如,以下语句不会返回错误:

                              SELECT CONCAT(utf8_column, latin1_column) FROM t1;
                              

                              它返回具有utf8 字符集和与utf8_column 相同的排序规则的结果。 latin1_column 的值在连接之前会自动转换为 utf8

                            • 对于操作数来自相同字符集但混合了_bin 归类和_ci_cs 归类的操作,使用_bin 归类。这类似于混合非二进制字符串和二进制字符串的操作如何将操作数评估为二进制字符串,只是它用于排序规则而不是数据类型。

                        3. 那么什么是“非法的排序规则组合”?

                          当一个表达式比较两个具有不同归类但具有相同强制力的字符串时,就会出现“归类的非法混合”,并且强制规则无法帮助解决冲突。就是上述引文第三点所描述的情况。

                          问题中给出的特定错误Illegal mix of collations (latin1_general_cs,IMPLICIT) and (latin1_general_ci,IMPLICIT) for operation '=' 告诉我们,两个具有相等强制力的非Unicode 字符串之间存在相等比较。它还告诉我们,排序规则没有在语句中明确给出,而是从字符串的来源(例如列元数据)中暗示出来的。

                        4. 这一切都很好,但是如何解决这些错误呢?

                          正如上面引用的手册摘录所暗示的,这个问题可以通过多种方式解决,其中两种是明智的,值得推荐:

                          • 更改一个(或两个)字符串的排序规则,使它们匹配并且不再有任何歧义。

                            如何做到这一点取决于字符串的来源:文字表达式采用collation_connection系统变量中指定的排序规则;表中的值采用列元数据中指定的排序规则。

                          • 强制一个字符串不可强制。

                            我从上面省略了以下引用:

                            MySQL 分配强制值如下:

                            • 显式 COLLATE 子句的强制力为 0。(根本没有强制力。)

                            • 两个具有不同排序规则的字符串的连接具有 1 的强制力。

                            • 列或存储的例程参数或局部变量的排序规则具有 2 的强制力。

                            • “系统常量”(USER()VERSION() 等函数返回的字符串)的强制力为 3。

                            • 文字的排序规则的强制力为 4。

                            • NULL 或从NULL 派生的表达式具有 5 的强制力。

                            因此,只需将COLLATE 子句添加到比较中使用的字符串之一将强制使用该排序规则。

                          如果仅仅为了解决此错误而部署其他方法,则将是非常糟糕的做法:

                          • 强制其中一个(或两个)字符串具有其他强制值,以便优先。

                            使用CONCAT()CONCAT_WS() 将产生一个强制力为1 的字符串;并且(如果在存储的例程中)使用参数/局部变量将导致字符串的强制力为 2。

                          • 更改一个(或两个)字符串的编码,使一个是 Unicode,另一个不是。

                            这可以通过使用CONVERT(<em>expr</em> USING <em>transcoding_name</em>) 进行转码来完成;或通过更改数据的底层字符集(例如,修改列,更改 character_set_connection 的文字值,或以不同的编码从客户端发送它们并更改 character_set_client / 添加字符集介绍器)。请注意,如果某些所需字符无法在新字符集中进行编码,则更改编码会导致其他问题。

                          • 更改一个(或两个)字符串的编码,使它们都相同,并更改一个字符串以使用相关的_bin 排序规则。

                            上面已经详细介绍了更改编码和排序规则的方法。如果实际上需要应用比_bin 排序规则提供的更高级的排序规则,那么这种方法几乎没有用处。

                        【讨论】:

                        • 请注意,当不应该使用哪种排序规则时,也会出现“非法混合排序规则”,但要强制转换的字符串必须转码为一种编码,其中一些无法表示字符。我在a previous answer讨论过这个案例。
                        • 很好的答案。这个应该更进一步,因为它深入探讨了开发人员应该真正了解的内容;不仅仅是如何解决它,而是真正理解为什么事情会以他们正在发生的方式发生。
                        • 谢谢老兄,你今天教会了我一些东西。
                        • 很好的答案,信息量很大。有时 SO 对“为什么它不起作用”的内容太少,而主要是关于“复制并粘贴它以使其工作”。
                        【解决方案16】:

                        我用了ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;,但是没用。

                        在这个查询中:

                        Select * from table1, table2 where table1.field = date_format(table2.field,'%H');
                        

                        这对我有用:

                        Select * from table1, table2 where concat(table1.field) = date_format(table2.field,'%H');
                        

                        是的,只有concat

                        【讨论】:

                        • 检查表及其列的排序规则(显示表状态;并显示表 1 中的完整列;)。如果表已经使用错误的排序规则创建,则使用 alter database 将不起作用。
                        • ALTER DATABASE mydb DEFAULT COLLATE ...为我工作,所以赞成。也许我有一个优势,因为我可以删除并重新创建数据库并从备份中加载。
                        【解决方案17】:

                        MySQL 真的不喜欢混合排序规则,除非它可以强制它们使用相同的排序规则(这在您的情况下显然是不可行的)。您不能通过COLLATE clause 强制使用相同的排序规则吗? (或者更简单的BINARY 快捷方式,如果适用的话...)。

                        【讨论】:

                        • 这是 MySQL 独有的吗?其他系统如何处理显然具有相同优先级的不兼容排序规则的混合?
                        • 您的链接无效。
                        猜你喜欢
                        • 1970-01-01
                        • 2010-11-03
                        • 2017-10-19
                        相关资源
                        最近更新 更多