在MySQL中排除“非法混用整理”错误

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

非法混合sorting(latin1_general_cs,IMPLICIT)和(latin1_general_ci,IMPLICIT)进行操作'='

有什么想法在这里可能会出错?

该表的sorting规则是latin1_general_ci ,where子句中的列是latin1_general_cs

这通常是通过比较两个不兼容的归类string或尝试将不同归类的数据select到组合列引起的。

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

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

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

您的解决scheme是为查询中的两列指定共享sorting规则。 以下是COLLATE子句的一个使用示例:

 SELECT * FROM table ORDER BY key COLLATE latin1_general_ci; 

另一种select是使用BINARY运算符:

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

你的解决scheme可能看起来像这样:

 SELECT * FROM table WHERE BINARY a = BINARY b; 

要么,

 SELECT * FROM table ORDER BY BINARY a; 

TL; DR

或者更改一个(或两个)string的sorting规则以使它们匹配,否则将COLLATE子句添加到您的expression式中。


  1. 这是什么“整理”的东西吗?

    正如“ 字符集和归类总则”中logging的那样 :

    字符集是一组符号和编码。 sorting规则是一组比较字符集中字符的规则。 我们用一个虚构的字符集的例子来明确区分。

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

    假设我们要比较两个string值“ A ”和“ B ”。 最简单的方法是查看编码:0代表“ A ”,1代表“ B ”。 由于0小于1,我们说“ A ”小于“ B ”。 我们刚刚做的是将一个sorting规则应用到我们的字符集。 sorting规则是一组规则(在这种情况下只有一个规则):“比较编码”。我们把所有可能sorting中最简单的一个称为二进制sorting。

    但是如果我们想说小写字母和大写字母是等价的呢? 那么我们至less有两条规则:(1)把小写字母“ a ”和“ b ”等同于“ A ”和“ B ”; (2)然后比较编码。 我们称之为不区分大小写的整理。 这比二进制sorting规则稍微复杂一些。

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

    更多的例子在sorting效应的例子中给出。

  2. 好吧,但MySQL如何决定给定expression式使用哪种sorting规则?

    如“expression式整理”中所logging:

    在绝大多数的语句中,很明显MySQL用来parsing一个比较操作的sorting规则。 例如,在以下情况下,应该清楚sorting规则是列charset_name的sorting规则:

     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或string文字'Y'的sorting规则? x'Y'都有sorting规则,那么哪个sorting规则优先?

    标准SQL使用以前被称为“可压缩性”规则来解决这些问题。

      [ deletia ] 

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

    • 使用最低的压制度值进行校对。

    • 如果双方具有相同的强制性,则:

      • 如果双方都是Unicode的,或者双方都不是Unicode,那是错误的。

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

         SELECT CONCAT(utf8_column, latin1_column) FROM t1; 

        它返回一个结果,该结果具有utf8的字符集和与utf8_column相同的sortingutf8_column 。 在连接之前, latin1_column值会自动转换为utf8

      • 对于来自相同字符集的操作数的操作,将_bin归类和_ci_cs归类混合,则使用_bin归类。 这与混合非二进制string和二进制string的操作如何将操作数评估为二进制string类似,除了用于整理而不是数据types之外。

  3. 那么什么是“非法组合”?

    当一个expression式比较不同sorting规则但是具有相同的可压缩性的两个string时,就会出现“非法sorting”,而强制性规则无法帮助解决冲突。 上面引用的第三点就是这种情况。

    问题中给出的特定错误Illegal mix of collations (latin1_general_cs,IMPLICIT) and (latin1_general_ci,IMPLICIT) for operation '='告诉我们两个具有相等可压缩性的非Unicodestring之间的等同比较。 此外,它还告诉我们,在声明中没有明确列出sorting规则,而是从string源(如列元数据)中隐含。

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

    正如上面引用的手册摘录所表明的那样,这个问题可以通过很多方法来解决,其中两个是合理的,并且是值得推荐的:

    • 改变一个(或两个)string的sorting规则,使它们匹配,不再有任何歧义。

      如何做到这一点取决于string来自哪里:文字expression式采用collation_connection系统variables中指定的collation_connection ; 来自表格的值采用列元数据中指定的sorting规则。

    • 强制一个string不能强制。

      我省略了以上的引述:

      MySQL分配如下的可执行性值:

      • 一个明确的COLLATE子句具有0的可压缩性(完全不能强制)。

      • 两个不同sorting规则的string的连接具有1的可压缩性。

      • 列或存储的例程参数或局部variables的sorting规则具有2的可压缩性。

      • “系统常量”(由USER()VERSION()等函数返回的string)具有3的可压缩性。

      • 一个文字的整理有一个4的强制性。

      • NULL或从NULL派生的expression式的可执行性为5。

      因此,简单地将COLLATE子句添加到比较中使用的某个string将强制使用该sorting规则。

    而其他人如果只是为了解决这个错误而部署起来会是非常糟糕的做法:

    • 强制一个(或两个)string具有一些其他的强制性值,以便优先考虑。

      CONCAT()CONCAT_WS()会导致一个具有1的强制性的string。 和(如果在存储例程中)使用参数/局部variables将导致具有2的可压缩性的string。

    • 更改一个(或两个)string的编码,使其中一个是Unicode,另一个不是。

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

    • 更改一个(或两个)string的编码,使它们都相同,并更改一个string以使用相关的_binsorting规则。

      上面详细介绍了改变编码和整理的方法。 如果实际需要应用比_bin归类所提供的更高级的归类规则,则这种方法几乎没有用处。

将我的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 | ... 

请注意,视图具有NULLsorting规则。 即使该查询对于一个视图显示为空,看起来视图和函数也具有sorting规则定义。 使用的sorting规则是在创build视图/函数时定义的数据库sorting规则。

可悲的解决scheme是同时更改数据库归类和重新创build视图/函数强制他们使用当前的sorting规则。

  • 更改数据库的sorting规则:

     ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci; 

我希望这会帮助别人。

有时候,转换字符集,特别是数据量巨大的数据库可能会很危险。 我认为最好的select是使用“二元”运算符:

 eg : WHERE binary table1.column1 = binary table2.column1 

你可以试试这个脚本 ,将所有的数据库和表转换为utf8。

解决scheme,如果涉及文字。

我正在使用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',……'创build的。根据这样的手册文字,应该从正确定义为'latin1'和'latin1_general_cs'的服务器设置inheritance它们的字符集和sorting规则。显然没有发生,我强迫它在创造的观点

 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两列,错误已经消失。 🙂

如果您遇到问题的列是“散列”,请考虑以下事项…

如果“散列”是一个二进制string,你应该真的使用BINARY(...)数据types。

如果“散列”是一个hexstring,你不需要utf8,并应该避免这种情况,因为字符检查等。例如,MySQL的MD5(...)产生一个固定长度的32字节hexstring。 SHA1(...)给出一个40字节的hexstring。 这可以存储在CHAR(32) CHARACTER SET ascii (或sha1的40)中。

或者更好的办法是把UNHEX(MD5(...))存入BINARY(16) 。 这削减了列的一半大小。 (但是,它确实使它不能打印。) SELECT HEX(hash) ...如果你想要它可读。

比较两个BINARY列没有sorting问题。

我有一个类似的问题,试图对stringvariables使用FIND_IN_SET过程。

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

并收到错误

错误代码:1267.对于操作“find_in_set”,sorting规则(utf8_unicode_ci,IMPLICIT)和(utf8_general_ci,IMPLICIT)

简短的回答:

无需更改任何collat​​ion_YYYYvariables,只需在variables声明旁边添加正确的sorting规则,即

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

很长的回答:

我首先检查了整理variables:

 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; 

这意味着我的variables被configuration为utf8_general_ci的默认sorting规则,而我的表被configuration为utf8_unicode_ci

通过在variables声明旁边添加COLLATE命令,variables归类匹配为表configuration的归类。

MySQL真的不喜欢混合sorting规则,除非它们强迫它们(在你的情况下显然不可行)。 难道你不能强制通过COLLATE子句使用相同的sorting规则吗? (或更简单的BINARY快捷方式,如果适用…)。

一个可能的解决scheme是将整个数据库转换为UTF8 (另见这个问题 )。

sorting问题的另一个来源是mysql.proc表。 检查存储过程和function的sorting规则:

 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列。

我用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

这段代码需要放在数据库的Run SQL查询/查询里面

SQL QUERY WINDOW

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

请用适当的名称replacetable_name和column_name。