Kết hợp bất hợp pháp các collations (utf8_unicode_ci, IMPLICIT) và (utf8_general_ci, IMPLICIT) cho hoạt động '='


160

Thông báo lỗi trên MySql:

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

Tôi đã trải qua một số bài viết khác và không thể giải quyết vấn đề này. Phần bị ảnh hưởng là một cái gì đó tương tự như thế này:

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;

Quy trình được lưu trữ mà tôi đang sử dụng là:

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

Tôi đã thử nghiệm với php, nhưng lỗi tương tự được đưa ra với SQLyog. Tôi cũng đã thử nghiệm tạo lại toàn bộ DB nhưng không tốt.

Bất kỳ trợ giúp sẽ được nhiều đánh giá cao.

Câu trả lời:


220

Đối chiếu mặc định cho các tham số thủ tục được lưu trữ là utf8_general_civà bạn không thể trộn các đối chiếu, do đó bạn có bốn tùy chọn:

Tùy chọn 1 : thêm COLLATEvào biến đầu vào của bạn:

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

Tùy chọn 2 : thêm COLLATEvào WHEREmệnh đề:

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

Tùy chọn 3 : thêm nó vào INđịnh nghĩa tham số:

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

Tùy chọn 4 : thay đổi trường chính nó:

ALTER TABLE users CHARACTER SET utf8 COLLATE utf8_general_ci;

Trừ khi bạn cần sắp xếp dữ liệu theo thứ tự Unicode, tôi sẽ đề nghị thay đổi tất cả các bảng của bạn để sử dụng utf8_general_ciđối chiếu, vì nó không yêu cầu thay đổi mã và sẽ tăng tốc độ sắp xếp lên một chút.

CẬP NHẬT : utf8mb4 / utf8mb4_unicode_ci hiện là phương thức đặt / đối chiếu ký tự ưa thích. utf8_general_ci được khuyên dùng, vì cải thiện hiệu suất là không đáng kể. Xem https://stackoverflow.com/a/766996/1432614


1
Cũng có thể thêm COLLATE utf8_unicode_civào hằng chuỗi : SET @EMAIL = 'abc@def.com' COLLATE utf8_unicode_ci;. Điều này đặc biệt hữu ích nếu bạn đang chạy tập lệnh từ bảng điều khiển, trong đó mã hóa mặc định của bảng điều khiển áp dụng cho đối chiếu các hằng chuỗi của bạn.
gaborsch

Hoặc bỏ cơ sở dữ liệu và tạo mới với utf8_general_ci; đối chiếu.
Oleksii Kyslytsyn

2
Để tham khảo trong tương lai, đừng thay đổi tất cả các bảng của bạn thành utf8_general_ci trừ khi bạn hiểu sự khác biệt giữa hai lần đối chiếu.
Manatax

1
@GaborSch Thêm collate vào biến chuỗi là giải pháp cho tôi, tôi đã viết một câu trả lời chi tiết về nó trước khi tôi nhận thấy bình luận của bạn.
nkatsar

tôi nhận được cùng một lỗi, ngoại trừ (utf8mb4_unicode_ci, IMPLICIT)thay vì (utf8_unicode_ci, IMPLICIT). Tôi đang loại bỏ dữ liệu trên web bằng python, sau đó tạo tệp CSV với dữ liệu bị loại bỏ, sau đó tôi xử lý tệp PHP trên máy chủ của mình để tải dữ liệu lên cơ sở dữ liệu của tôi. tất cả các bảng / cột MySQL của tôi được đối chiếu là utf8mb4_unicode_ci. vấn đề có thể phát sinh vì tôi mã hóa dữ liệu như utf8trong python / csv?
oldboy

27

Tôi đã dành nửa ngày để tìm kiếm câu trả lời cho một lỗi "hỗn hợp bất hợp pháp" giống hệt nhau với các xung đột giữa utf8_unicode_ci và utf8_general_ci.

Tôi thấy rằng một số cột trong cơ sở dữ liệu của tôi không được đối chiếu cụ thể utf8_unicode_ci . Có vẻ như mysql đã đối chiếu các cột này utf8_general_ci .

Cụ thể, chạy truy vấn 'SHOW CREATE TABLE table1' đã xuất ra một cái gì đó như sau:

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

Lưu ý dòng 'col1' varchar (4) CHARACTER SET utf8 KHÔNG NULL không có đối chiếu được chỉ định. Sau đó tôi đã chạy truy vấn sau:

ALTER TABLE table1 CHANGE col1 col1 VARCHAR(4) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;

Điều này đã giải quyết lỗi "hỗn hợp bất hợp pháp" của tôi. Hy vọng điều này có thể giúp đỡ người khác ngoài đó.


7
cảm ơn. 'HIỂN THỊ TẠO BẢNG' là cách dễ nhất để hiểu và khắc phục nguyên nhân gốc rễ của vấn đề.
joro

2
Cũng lưu ý rằng việc chỉ định COLLATEcho toàn bộ bảng (nghĩa là ALTER TABLE table1 CHARSET utf8 COLLATE utf8_unicode_ci) sẽ không khắc phục được sự cố , nó phải được thực hiện cho từng cột (có vấn đề).
Skippy le Grand Gourou

6

Tôi đã có một vấn đề tương tự, nhưng nó xảy ra với tôi trong thủ tục, khi tham số truy vấn của tôi được đặt bằng biến, ví dụ SET @value='foo'.

Điều gây ra điều này là không khớp collation_connectionvà cơ sở dữ liệu đối chiếu. Thay đổi collation_connectionđể phù hợp collation_databasevà vấn đề đã biến mất. Tôi nghĩ rằng đây là cách tiếp cận thanh lịch hơn so với việc thêm THU THẬP sau param / value.

Để tổng hợp: tất cả các bộ sưu tập phải phù hợp. Sử dụng SHOW VARIABLESvà đảm bảo collation_connectioncollation_databasekhớp (cũng kiểm tra đối chiếu bảng bằng cách sử dụng SHOW TABLE STATUS [table_name]).


1
Vấn đề tương tự cũng xảy ra với tôi, tôi đã tránh thay đổi các biến collation_YYY bằng cách đặt đối chiếu trực tiếp trong khai báo biến. SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
nkatsar

5

Một chút tương tự như câu trả lời @bpile, trường hợp của tôi là cài đặt mục nhập my.cnf collation-server = utf8_general_ci. Sau khi tôi nhận ra rằng (và sau khi thử mọi thứ ở trên), tôi mạnh mẽ chuyển cơ sở dữ liệu của mình sang utf8_general_ci thay vì utf8_unicode_ci và đó là:

ALTER DATABASE `db` CHARACTER SET utf8 COLLATE utf8_general_ci;

1
Thật kỳ lạ khi các cấu hình được trải rộng xung quanh rất nhiều. Tất cả các mặc định đối chiếu nên được đặt trên cùng một vị trí.
Manatax 7/12/2016

0

Trong trường hợp của riêng tôi, tôi có lỗi sau

Kết hợp bất hợp pháp các collations (utf8_general_ci, IMPLICIT) và (utf8_unicode_ci, IMPLICIT) cho hoạt động '='

$ this-> db-> select ("users.username là matric_no, CONCAT (users.surname, '', users.first_name, '', users.last_name) làm tên đầy đủ") -> tham gia ('users', 'users .username = class_students.matric_no ',' left ') -> where (' class_students.session_id ', $ session) -> ở đâu );

Sau nhiều tuần tìm kiếm trên google, tôi nhận thấy hai lĩnh vực tôi đang so sánh bao gồm tên đối chiếu khác nhau. Tên người dùng đầu tiên là utf8_general_ci trong khi tên thứ hai là utf8_unicode_ci vì vậy tôi quay lại cấu trúc của bảng thứ hai và thay đổi trường thứ hai (matric_no) thành utf8_general_ci và nó hoạt động như một bùa mê.


0

Mặc dù tìm thấy một số lượng lớn câu hỏi về cùng một vấn đề ( 1 , 2 , 3 , 4 ) tôi chưa bao giờ tìm thấy câu trả lời nào được xem xét về hiệu suất, ngay cả ở đây.

Mặc dù nhiều giải pháp làm việc đã được đưa ra nhưng tôi muốn xem xét hiệu suất.

EDIT: Cảm ơn Manatax đã chỉ ra rằng tùy chọn 1 không gặp phải vấn đề về hiệu suất.

Sử dụng Tùy chọn 1 và 2 , còn gọi là phương pháp đúc COLLATE , có thể dẫn đến tắc nghẽn tiềm ẩn, vì bất kỳ chỉ mục nào được xác định trên cột sẽ không được sử dụng gây ra quét toàn bộ .

Mặc dù tôi đã không thử lựa chọn 3 , nhưng linh cảm của tôi là nó sẽ chịu hậu quả tương tự của lựa chọn 1 và 2.

Cuối cùng, Tùy chọn 4 là tùy chọn tốt nhất cho các bảng rất lớn khi khả thi. Tôi có nghĩa là không có cách sử dụng khác dựa trên đối chiếu ban đầu.

Xem xét truy vấn đơn giản hóa này:

SELECT 
    *
FROM
    schema1.table1 AS T1
        LEFT JOIN
    schema2.table2 AS T2 ON T2.CUI = T1.CUI
WHERE
    T1.cui IN ('C0271662' , 'C2919021')
;

Trong ví dụ ban đầu của tôi, tôi đã tham gia nhiều hơn nữa. Tất nhiên, bảng1 và bảng2 có các đối chiếu khác nhau. Sử dụng toán tử đối chiếu để truyền, nó sẽ dẫn đến các chỉ mục không được sử dụng.

Xem giải thích sql trong hình dưới đây.

Giải thích truy vấn trực quan khi sử dụng diễn viên COLLATE

Mặt khác, tùy chọn 4 có thể tận dụng lợi thế của chỉ mục có thể và dẫn đến các truy vấn nhanh.

Trong hình bên dưới, bạn có thể thấy cùng một truy vấn đang được chạy sau khi áp dụng Tùy chọn 4 , hay còn gọi là thay đổi đối chiếu lược đồ / bảng / cột.

Giải thích truy vấn trực quan sau khi đối chiếu đã được thay đổi và do đó không có diễn viên đối chiếu

Tóm lại, nếu hiệu suất là quan trọng và bạn có thể thay đổi đối chiếu của bảng, hãy chọn Tùy chọn 4 . Nếu bạn phải hành động trên một cột duy nhất, bạn có thể sử dụng một cái gì đó như thế này:

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

Cảm ơn bạn đã đóng góp Raffaele, nhưng tôi tin rằng tùy chọn 1 sẽ sử dụng chỉ mục, bởi vì bạn không truyền bảng, nhưng giá trị so sánh trước khi bạn chuyển nó đến SP.
Manatax

Cảm ơn đã chỉ ra rằng. Đó là sai lầm của tôi. Tôi chỉnh sửa câu trả lời của tôi cho phù hợp.
Raffaele

0

Điều này xảy ra khi một cột được đặt rõ ràng thành một đối chiếu khác nhau hoặc đối chiếu mặc định là khác nhau trong bảng được truy vấn.

nếu bạn có nhiều bảng bạn muốn thay đổi đối chiếu khi chạy truy vấn này:

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;

điều này sẽ xuất ra các truy vấn cần thiết để chuyển đổi tất cả các bảng để sử dụng đối chiếu chính xác cho mỗi cột


Nó cũng xảy ra khi (như trong trường hợp của tôi) đối chiếu mặc định của bạn cho SP khác với đối chiếu được sử dụng cho bảng được truy vấn.
Manatax
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.