解决MySQL中的“排序规则的非法混合”错误


210

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

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

您对这里可能出什么问题有任何想法吗?

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


2
我一直在使用各种数据库(自1990年以来),并且NySQL对排序规则和强制性的使用似乎是“疯狂的”,数据库解决了为数据库强加“ ONE”字符集的问题,用于从数据库转换为唯一字符集的导入/导出过程。Mysql选择的解决方案是一个破坏性的解决方案,因为它将“应用程序问题”(字符集转换)与数据库问题(排序规则用法)混合在一起。为什么不从数据库中“删除”那些愚蠢而繁琐的功能,从而使它变得更加有用和可控,就可以了
Maurizio Pievaioli

Answers:


216

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

该子句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;

2
谢谢。实际上,就我而言,这似乎很不可思议。当我按原样运行查询时,可以通过查询浏览器获取结果。但是使用存储过程会引发错误。
user355562 2010年

5
二进制似乎是对我最好的解决方案。如果您不使用任何棘手的过滤器,那么对您来说也可能是最好的选择。
亚当F

我有同样的问题,解决这个问题的方法是从头开始重新创建。我尝试更改排序规则,但是当我加入时仍然出现错误,所以我尝试了这种方式。cmiiw
Bobby Z

请注意,MariaDB中存在一个错误, COLLATE latin1_general_ci 该错误会导致另一个错误:COLLATION 'utf8_general_ci' is not valid for CHARACTER SET 'latin1''-即使您没有包含CHARACTER SET'latin1'的列!解决方案是使用BINARY类型转换。另请参阅此问题
Mel_T

154

TL; DR

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


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

    一般字符集和排序规则中所述

    字符集是一组符号和编码。甲归类为在一个字符集的字符进行比较的一组规则。让我们用一个假想字符集的例子来使区别清楚。

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

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

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

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

    “整理效果的示例”下给出了更多示例。

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

    “表达式排序规则”中所述

    在绝大多数语句中,很明显MySQL使用什么排序规则来解决比较操作。例如,在以下情况下,应该清楚排序规则是column的排序规则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使用以前称为“强制性”规则的方式解决了此类问题。

    [ 删除 ]

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

    • 使用具有最低矫顽力值的排序规则。

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

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

      • 如果其中一方具有Unicode字符集,而另一方具有非Unicode字符集,则以具有Unicode字符集的一方为准,并且自动字符集转换将应用于非Unicode一方。例如,以下语句不返回错误:

        SELECT CONCAT(utf8_column, latin1_column) FROM t1;

        它返回一个结果,该结果的字符集为utf8,排序规则与相同utf8_column。的值latin1_columnutf8在连接前自动转换为。

      • 对于具有相同字符集但混合使用_bin归类和a _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


4
请注意,当不存在应使用排序规则的歧义时,也可能出现“排序规则的非法混合”,但是必须将要强制执行的字符串转换为无法表示其某些字符的编码。我已经在先前的回答中讨论过这种情况。
eggyal 2014年

5
好答案。这应该是更深层次的,因为它深入了开发人员应真正了解的内容。不仅解决问题,而且真正了解事情为什么以这种方式发生。
标记

谢谢你,老兄,你今天教我一些东西。
briankip 2015年

66

将我的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排序规则。似乎该视图和函数具有排序规则定义,即使此查询显示一个视图为null。使用的排序规则是在创建视图/函数时定义的数据库排序规则。

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

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

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

    ALTER TABLE mydb CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

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


12
排序规则也可以设置在列级别。您可以通过以下方式查看它:show full columns from my_table;
Jonathan Tran 2012年

谢谢。我只是删除了该架构,并使用正确的默认排序规则重新创建了该架构,然后重新导入了所有内容。
JRun,2014年

1
@JonathanTran谢谢!我在所有表,数据库和连接上都设置了字符集和排序规则,但是仍然出现错误!排序规则未设置在列上!我用alter table <TABLE> modify column <COL> varchar(255) collate utf8_general_ci;
克洛伊(Chloe)

2
未来的Google员工的旁注:即使您的数据库,表和字段都具有相同的排序规则,也必须确保您的连接使用的是相同的排序规则。一切都有»utf8mb4_unicode_ci«,但SHOW session variables like '%collation%';告诉您»collat​​ion_connection«是»utf8mb4_general_ci«?然后SET collation_connection = utf8mb4_unicode_ci事先运行。
pixelbrackets

谢谢!花了我一段时间来追踪。这些表不仅必须具有相同的排序规则,而且数据库也必须具有相同的排序规则!
moto

15

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

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

10

我有一个类似的问题,试图将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命令,变量归类与为表配置的归类匹配。



2

如果涉及文字,则为解决方案。

我正在使用Pentaho Data Integration,但未指定SQL语法。使用非常简单的数据库查找,出现错误“操作'='的排序规则(cp850_general_ci,COERCIBLE)和(latin1_swedish_ci,COERCIBLE)的排序规则非法混合”

生成的代码是“从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,错误已消失。:)


1

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


这是MySQL特有的吗?其他系统如何处理似乎优先级相同的不兼容排序规则的混合?
eggyal 2014年

您的链接无效。
Benubird 2014年

1

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

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

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

或者,最好将其存储UNHEX(MD5(...))到中BINARY(16)。这样可将色谱柱尺寸减小一半。(但是,它确实使它无法打印。) SELECT HEX(hash) ...如果您希望它可读。

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


1

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

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

使用以下命令指导您:

SHOW VARIABLES LIKE "collation_database";
SHOW TABLE STATUS;

现在,如果您喜欢在各处添加“整理”,并用强制填充“覆盖”来增强代码,请问是我的猜测。



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



-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


检查表及其列的排序规则(显示表状态;并显示table1中的完整列;)。如果已使用错误的排序规则创建了表,则无法使用alter database。
Ariel T

ALTER DATABASE mydb DEFAULT COLLATE ...对我有用,所以赞成。也许我有一个优势,因为我可以删除并重新创建数据库并从备份中加载。
tobixen

-2

这段代码需要放入数据库中的“运行SQL查询/查询”中

SQL查询窗口

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。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.