关于mysql:操作’=’的非法混合排序(utf8_unicode_ci,IMPLICIT)和(utf8_general_ci,IMPLICIT)

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

MySQL上的错误消息:

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

我已经读过其他几篇文章,但没能解决这个问题。受影响的部分与此类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
CREATE TABLE users (
    userID INT UNSIGNED NOT NULL AUTO_INCREMENT,
    firstName VARCHAR(24) NOT NULL,
    lastName VARCHAR(24) NOT NULL,
    username VARCHAR(24) NOT NULL,
    password VARCHAR(40) NOT NULL,
    PRIMARY KEY (userid)
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE products (
    productID INT UNSIGNED NOT NULL AUTO_INCREMENT,
    title VARCHAR(104) NOT NULL,
    picturePath VARCHAR(104) NULL,
    pictureThumb VARCHAR(104) NULL,
    creationDate DATE NOT NULL,
    closeDate DATE NULL,
    deleteDate DATE NULL,
    varPath VARCHAR(104) NULL,
    isPublic TINYINT(1) UNSIGNED NOT NULL DEFAULT '1',
    PRIMARY KEY (productID)
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE productUsers (
    productID INT UNSIGNED NOT NULL,
    userID INT UNSIGNED NOT NULL,
    permission VARCHAR(16) NOT NULL,
    PRIMARY KEY (productID,userID),
    FOREIGN KEY (productID) REFERENCES products (productID) ON DELETE RESTRICT ON UPDATE NO ACTION,
    FOREIGN KEY (userID) REFERENCES users (userID) ON DELETE RESTRICT ON UPDATE NO ACTION
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

我使用的存储过程是:

1
2
3
4
5
6
7
8
9
CREATE PROCEDURE updateProductUsers (IN rUsername VARCHAR(24),IN rProductID INT UNSIGNED,IN rPerm VARCHAR(16))
BEGIN
    UPDATE productUsers
        INNER JOIN users
        ON productUsers.userID = users.userID
        SET productUsers.permission = rPerm
        WHERE users.username = rUsername
        AND productUsers.productID = rProductID;
END

我在用PHP进行测试,但sqlyog也有同样的错误。我还测试了重新创建整个数据库,但是没有用。

任何帮助都将不胜感激。


有四种选择:

选项1:将COLLATE添加到输入变量:

1
2
SET @rUsername = ‘aname’ COLLATE utf8_unicode_ci; -- COLLATE added
CALL updateProductUsers(@rUsername, @rProductID, @rPerm);

方案2:在WHERE条款中增加COLLATE

1
2
3
4
5
6
7
8
9
10
11
12
CREATE PROCEDURE updateProductUsers(
    IN rUsername VARCHAR(24),
    IN rProductID INT UNSIGNED,
    IN rPerm VARCHAR(16))
BEGIN
    UPDATE productUsers
        INNER JOIN users
        ON productUsers.userID = users.userID
        SET productUsers.permission = rPerm
        WHERE users.username = rUsername COLLATE utf8_unicode_ci -- COLLATE added
        AND productUsers.productID = rProductID;
END

选项3:添加到IN参数定义中:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE PROCEDURE updateProductUsers(
    IN rUsername VARCHAR(24) COLLATE utf8_unicode_ci, -- COLLATE added
    IN rProductID INT UNSIGNED,
    IN rPerm VARCHAR(16))
BEGIN
    UPDATE productUsers
        INNER JOIN users
        ON productUsers.userID = users.userID
        SET productUsers.permission = rPerm
        WHERE users.username = rUsername
        AND productUsers.productID = rProductID;
END

选项4:更改字段本身:

1
ALTER TABLE users CHARACTER SET utf8 COLLATE utf8_general_ci;

由于存储过程参数的默认排序规则是utf8_general_ci,因此不能混合排序。

更新:utf8mb4/utf8mb4_unicode_ci现在是首选的字符集/排序规则方法。建议不要使用utf8_-general_-ci,因为性能改进可以忽略不计。请参阅https://stackoverflow.com/a/766996/1432614


我花了半天时间寻找一个完全相同的"非法混合排序"错误的答案,这个错误与utf8 Unicode CI和utf8 General CI之间存在冲突。

我发现我数据库中的一些列没有经过专门的utf8_unicode_ci排序。MySQL似乎隐式地对这些列进行了utf8_-general_-ci排序。

具体来说,运行"show create table table1"查询输出如下内容:

1
2
3
4
5
6
| table1 | CREATE TABLE `table1` (
`id` int(11) NOT NULL,
`col1` varchar(4) CHARACTER SET utf8 NOT NULL,
`col2` int(11) NOT NULL,
PRIMARY KEY (`col1`,`col2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

注意行'col1'varchar(4)character set utf8 not null没有指定排序规则。然后我运行了以下查询:

埃多克斯1〔6〕

这解决了我的"非法混合排序"错误。希望这能帮助其他人。


我也遇到了类似的问题,但我在过程内部遇到了这个问题,当时我的查询参数是使用变量设置的,例如SET @value='foo'

造成这种情况的原因是collation_connection和数据库排序不匹配。把collation_connection改成与collation_database匹配,问题就消失了。我认为这比在参数/值之后添加collate更优雅。

总而言之:所有排序规则都必须匹配。使用SHOW VARIABLES并确保collation_connectioncollation_database匹配(同时使用SHOW TABLE STATUS [table_name]检查表排序)。


有点类似于@bpile answer,我的案例是my.cnf条目设置collation-server = utf8_general_ci。在我意识到这一点之后(在尝试了上述所有方法之后),我强制地将数据库切换到utf8_-general_-ci,而不是utf8_-unicode_-ci,就是这样:

1
ALTER DATABASE `db` CHARACTER SET utf8 COLLATE utf8_general_ci;


如果列显式设置为其他排序规则,或者查询的表中的默认排序规则不同,则会发生这种情况。

如果有许多表要在运行此查询时更改排序规则:

1
2
3
select concat('ALTER TABLE ', t.table_name , ' CONVERT TO CHARACTER
SET utf8 COLLATE utf8_unicode_ci;'
) from (SELECT table_name FROM
information_schema.tables where table_schema='SCHRMA') t;

这将输出转换所有表以使用每列正确排序规则所需的查询。


尽管在同一个问题(1,2,3,4)上发现了大量的问题,但我从来没有找到一个考虑性能的答案,即使在这里。

虽然已经给出了多个工作解决方案,但我想考虑一下性能。

编辑:感谢Manatax指出选项1不会出现性能问题。

使用选项1 and2,即collate cast方法,可能会导致潜在的瓶颈,因为在列上定义的任何索引都不会被使用,从而导致完全扫描。

尽管我没有尝试过选项3,但我的预感是,它将遭受与选项1和2相同的后果。

最后,在可行的情况下,选项4是非常大的表的最佳选择。我的意思是没有依赖于原始排序规则的其他用法。

考虑这个简化的查询:

1
2
3
4
5
6
7
8
9
SELECT
    *
FROM
    schema1.table1 AS T1
        LEFT JOIN
    schema2.table2 AS T2 ON T2.CUI = T1.CUI
WHERE
    T1.cui IN ('C0271662' , 'C2919021')
;

在我最初的示例中,我有更多的连接。当然,表1和表2有不同的排序规则。使用collate运算符强制转换,将导致未使用索引。

请参见下图中的SQL解释。

使用collate cast时的可视化查询解释

另一方面,选项4可以利用可能的索引并导致快速查询。

在下面的图片中,您可以看到相同的查询在应用选项4之后运行,也就是改变模式/表/列排序规则。

排序规则更改后的可视化查询解释,因此没有排序规则强制转换

总之,如果性能很重要并且您可以更改表的排序规则,那么选择选项4。如果必须对单个列执行操作,则可以使用如下内容:

1
ALTER TABLE schema1.table1 MODIFY `field` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;


在我自己的情况下,我有以下错误

操作"="的排序规则(utf8_-general_-ci,implicit)和(utf8_-unicode_-ci,implicit)的非法混合

$this->db->select("users.username as matric_no, CONCAT(users.surname,
' ',
users.first_name, ' ', users.last_name) as fullname")
->join('users', 'users.username=classroom_students.matric_no', 'left')
->where('classroom_students.session_id', $session)
->where('classroom_students.level_id', $level)
->where('classroom_students.dept_id', $dept);

经过数周的谷歌搜索,我注意到我比较的两个字段由不同的排序规则名称组成。第一个是utf8-general_-ci,第二个是utf8-unicode_-ci,所以我回到第二个表的结构中,把第二个字段(matric_-no)改成了utf8-general_-ci,它工作得很有魅力。