在mysql中排除“非法混合排序规则”错误

时间:2010-06-12 16:23:43

标签: mysql collation

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

  

操作'='的非法混合排序(latin1_general_cs,IMPLICIT)和(latin1_general_ci,IMPLICIT)

关于这里可能出现什么问题的任何想法?

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

17 个答案:

答案 0 :(得分:187)

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

条款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;

答案 1 :(得分:135)

TL; DR

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


  1. 这是什么"整理"东西呢?

    正如Character Sets and Collations in General所述:

      

    字符集是一组符号和编码。 排序规则是一组用于比较字符集中字符的规则。让我们用虚构字符集的例子来区分。

         

    假设我们有一个包含四个字母的字母:“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”。我们刚刚完成的是对我们的字符集应用排序规则。排序规则是一组规则(在这种情况下只有一个规则):“比较编码。”我们将所有可能的排序规则称为二进制排序规则。

         

    但是如果我们想说小写和大写字母是等价的呢?然后我们至少会有两条规则:(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(expr USING transcoding_name)进行转码来完成;或者通过更改数据的基础字符集(例如,修改列,更改character_set_connection的文字值,或者以不同的编码从客户端发送它们并更改character_set_client /添加字符集导入器) 。请注意,如果无法在新字符集中编码某些所需字符,则更改编码会导致其他问题。

    • 更改一个(或两个)字符串的编码,使它们相同并更改一个字符串以使用相关的_bin校对。

      上面已详细描述了改变编码和校对的方法。如果实际上需要应用比_bin排序规则提供的更高级的排序规则,这种方法几乎没用。

答案 2 :(得分:54)

将我的2c添加到未来googlers的讨论中。

我正在调查一个类似的问题,当我使用收到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 排序规则。即使此查询对于一个视图显示为null,视图和函数似乎也具有排序规则定义。使用的排序规则是在创建视图/函数时定义的DB排序规则。

令人遗憾的解决方案是更改db排序规则并重新创建视图/函数以强制它们使用当前排序规则。

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

    ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;
    

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

答案 3 :(得分:13)

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

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

答案 4 :(得分:7)

我遇到了类似的问题,试图将FIND_IN_SET程序与字符串变量一起使用。

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命令,变量collat​​ion与为表配置的排序规则匹配。

答案 5 :(得分:5)

您可以尝试this script,将所有数据库和表格转换为utf8。

答案 6 :(得分:2)

以下解决方案对我有用。

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

答案 7 :(得分:1)

解决方法是否涉及文字。

我正在使用Pentaho Data Integration,并且没有指定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,错误消失了。 :)

答案 8 :(得分:1)

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

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

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

或者,更好的是,将UNHEX(MD5(...))存储到BINARY(16)。这减少了一半的柱子大小。 (但它确实使它变得不可打印。)SELECT HEX(hash) ...如果你想让它可读的话。

比较两个BINARY列没有归类问题。

答案 9 :(得分:1)

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

答案 10 :(得分:0)

排序规则问题的另一个来源是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 :(得分:0)

可能的解决方案是convert the entire database to UTF8(另请参阅此question)。

答案 12 :(得分:0)

如果已安装phpMyAdmin,则可以按照以下链接中给出的说明进行操作:https://mediatemple.net/community/products/dv/204403914/default-mysql-character-set-and-collation您必须将数据库的排序规则与所有表的排序规则以及表和然后重新编译所有存储的过程和函数。这样,一切都应该再次工作。

答案 13 :(得分:0)

非常有趣...现在,准备好。我查看了所有“添加整理”解决方案,对我来说,这些都是创可贴修复程序。现实情况是数据库设计很“糟糕”。是的,添加了标准更改和新内容,等等,但这并不会改变错误的数据库设计事实。我拒绝采用在整个SQL语句中添加“整理”的路线,只是为了使我的查询正常工作。唯一对我有用的解决方案,实际上消除了将来对代码进行调整的需要,是重新设计数据库/表,以匹配我将在长期使用和使用的字符集。在这种情况下,我选择使用字符集“ utf8mb4 ”。

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

使用以下命令指导您:

SHOW VARIABLES LIKE "collation_database";
SHOW TABLE STATUS;

现在,如果您喜欢在各处添加“整理”,并通过强制“覆盖”来增强代码,请相信我。

答案 14 :(得分:0)

我个人在一个过程中遇到了这个问题。 如果您不想alter table,可以尝试将参数转换为过程。 我尝试将collat​​e(与set配合使用)进行局部使用,但对我没有用。

CONVERT(my_param USING utf32) 做到了。

答案 15 :(得分:-1)

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

答案 16 :(得分:-2)

此代码需要放在数据库

上运行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;

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