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

use*_*562 192 mysql collation

尝试通过MySQL中的存储过程进行选择时,我得到以下错误.

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

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

表的排序规则latin1_general_ci与where子句中的列的排序规则相同latin1_general_cs.

def*_*nes 199

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

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

例如,以下WHERE子句将始终给出您发布的错误:

WHERE 'A' COLLATE latin1_general_ci = 'A' COLLATE latin1_general_cs
Run Code Online (Sandbox Code Playgroud)

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

SELECT * FROM table ORDER BY key COLLATE latin1_general_ci;
Run Code Online (Sandbox Code Playgroud)

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

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

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

SELECT * FROM table WHERE BINARY a = BINARY b;
Run Code Online (Sandbox Code Playgroud)

要么,

SELECT * FROM table ORDER BY BINARY a;
Run Code Online (Sandbox Code Playgroud)

  • Binary似乎是我最好的解决方案.如果您没有使用任何棘手的过滤器,它可能是最好的. (5认同)
  • 谢谢.实际上,在我的情况下,它似乎表现得非常奇怪.当我按原样运行查询时,通过查询浏览器,它会获取结果.但是使用存储过程会引发错误. (2认同)
  • 请注意,转换列来比较它们将导致该列上的任何索引被忽略。 (2认同)

egg*_*yal 142

TL; DR

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


  1. 无论如何,这个"整理"的东西是什么?

    字符集和排序规则中所述:

    字符集是一组符号和编码.甲归类为在一个字符集的字符进行比较的一组规则.让我们用虚构字符集的例子来区分清楚.

    假设我们有一个带有四个字母的字母:" 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" 规则).

    整理效果的实施例下给出了进一步的实例.

  2. 好的,但MySQL如何决定用于给定表达式的排序规则?

    表达式排序规则所述:

    在绝大多数语句中,很明显MySQL用于解析比较操作的排序规则.例如,在以下情况中,应该清楚的是,排序规则是列的排序规则charset_name:

    SELECT x FROM T ORDER BY x;
    SELECT x FROM T WHERE x = x;
    SELECT DISTINCT x FROM T;
    
    Run Code Online (Sandbox Code Playgroud)

    但是,对于多个操作数,可能存在歧义.例如:

    SELECT x FROM T WHERE x = 'Y';
    
    Run Code Online (Sandbox Code Playgroud)

    比较应该使用列的整理x,还是字符串文字的整理'Y'?双方x'Y'有排序规则,所以其整理的优先级?

    标准SQL使用以前称为"强制性"规则的方法解决此类问题.

    [ deletia ]

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

    • 使用具有最低强制性值的排序规则.

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

      • 如果双方都是Unicode,或双方都不是Unicode,那就是错误.

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

        SELECT CONCAT(utf8_column, latin1_column) FROM t1;
        
        Run Code Online (Sandbox Code Playgroud)

        它返回一个具有字符集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_connectioncharacter_set_client

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

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

  • 请注意,当没有应该使用排序规则的歧义时,也会出现"非法混合排序",但是要强制的字符串必须转码为不能表示其某些字符的编码.我在[之前的回答](http://stackoverflow.com/a/12260863)中讨论了这个案例. (4认同)
  • 很好的答案.这应该是更进一步,因为它潜入了开发人员应该真正了解的内容; 不只是如何解决它,而是真正理解为什么事情正在以他们的方式发生;重新发生. (4认同)

Ari*_*l T 61

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

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

Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and 
(utf8_general_ci,IMPLICIT) for operation '='
Run Code Online (Sandbox Code Playgroud)

使用以下查询:

mysql> show variables like "collation_database";
    +--------------------+-----------------+
    | Variable_name      | Value           |
    +--------------------+-----------------+
    | collation_database | utf8_general_ci |
    +--------------------+-----------------+
Run Code Online (Sandbox Code Playgroud)

我能够告诉DB使用utf8_general_ci,而表是使用utf8_unicode_ci定义的:

mysql> show table status;
    +--------------+-----------------+
    | Name         | Collation       |
    +--------------+-----------------+
    | my_view      | NULL            |
    | my_table     | utf8_unicode_ci |
    ...
Run Code Online (Sandbox Code Playgroud)

请注意,视图具有NULL排序规则.即使此查询对于一个视图显示为null,视图和函数似乎也具有排序规则定义.使用的排序规则是在创建视图/功能时定义的DB排序规则.

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

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

  • 也可以在列级别设置排序规则.您可以使用以下命令查看它:`显示my_table中的完整列;` (11认同)
  • @JonathanTran 谢谢!我在所有表、数据库和连接上设置了字符集和排序规则,但它仍然给出错误!未在列上设置排序规则!我用“alter table <TABLE>修改列<COL> varchar(255) collat​​e utf8_general_ci;”修复了它 (2认同)
  • 未来googlers的旁注:即使您的数据库,表格和字段都具有相同的排序规则,您还必须确保您的连接使用相同的排序规则.一切都有»utf8mb4_unicode_ci«但是```SHOW会话变量如'%collat​​ion%';```告诉你»collat​​ion_connection«是»utf8mb4_general_ci«?然后事先运行```SET collat​​ion_connection = utf8mb4_unicode_ci```. (2认同)

小智 14

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

e.g : WHERE binary table1.column1 = binary table2.column1
Run Code Online (Sandbox Code Playgroud)


nka*_*sar 8

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

SET @my_var = 'string1,string2';
SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);
Run Code Online (Sandbox Code Playgroud)

并收到错误

错误代码:1267.操作'find_in_set'的非法混合排序(utf8_unicode_ci,IMPLICIT)和(utf8_general_ci,IMPLICIT)

简短回答:

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

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

答案很长:

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

mysql> SHOW VARIABLES LIKE 'collation%';
    +----------------------+-----------------+
    | Variable_name        | Value           |
    +----------------------+-----------------+
    | collation_connection | utf8_general_ci |
    +----------------------+-----------------+
    | collation_database   | utf8_general_ci |
    +----------------------+-----------------+
    | collation_server     | utf8_general_ci |
    +----------------------+-----------------+
Run Code Online (Sandbox Code Playgroud)

然后我检查了表格整理:

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;
Run Code Online (Sandbox Code Playgroud)

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

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


gar*_*ish 6

以下解决方案对我有用。

CONVERT( Table1.FromColumn USING utf8)    =  CONVERT(Table2.ToColumn USING utf8) 
Run Code Online (Sandbox Code Playgroud)


Mir*_*rak 5

您可以尝试使用此脚本将所有数据库和表转换为utf8.

  • 并且是一些索引大小的三倍. (2认同)

Ale*_*lli 2

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