การเปรียบเทียบที่ผิดกฎหมาย (utf8_unicode_ci, IMPLICIT) และ (utf8_general_ci, IMPLICIT) สำหรับการดำเนินงาน '='


160

ข้อความแสดงข้อผิดพลาดบน MySql:

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

ฉันได้อ่านบทความหลายเรื่องแล้วและไม่สามารถแก้ไขปัญหานี้ได้ ส่วนที่ได้รับผลกระทบมีลักษณะคล้ายกับสิ่งนี้:

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;

ขั้นตอนการจัดเก็บที่ฉันใช้อยู่คือ:

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 ฉันได้ทดสอบการสร้างฐานข้อมูลทั้งหมดขึ้นใหม่ แต่ไม่ดี

ความช่วยเหลือใด ๆ จะได้รับการชื่นชมมาก

คำตอบ:


220

การเปรียบเทียบค่าเริ่มต้นสำหรับพารามิเตอร์กระบวนงานที่เก็บไว้คือutf8_general_ciและคุณไม่สามารถผสมการเปรียบเทียบได้ดังนั้นคุณจึงมีสี่ตัวเลือก:

ตัวเลือกที่ 1 : เพิ่มCOLLATEให้กับตัวแปรอินพุตของคุณ:

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

ตัวเลือก 2 : เพิ่มCOLLATEไปยังWHEREข้อ:

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นิยามพารามิเตอร์:

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 : แก้ไขฟิลด์เอง:

ALTER TABLE users CHARACTER SET utf8 COLLATE utf8_general_ci;

หากคุณไม่ต้องการเรียงลำดับข้อมูลตามลำดับ Unicode ฉันขอแนะนำให้คุณเปลี่ยนตารางทั้งหมดเพื่อใช้การutf8_general_ciเรียงเนื่องจากไม่ต้องมีการเปลี่ยนแปลงรหัสและจะเพิ่มความเร็วในการเรียงลำดับเล็กน้อย

UPDATE : utf8mb4 / utf8mb4_unicode_ci ปัจจุบันเป็นวิธีการกำหนด / การเรียงชุดอักขระที่ต้องการ utf8_general_ci ไม่แนะนำเนื่องจากการปรับปรุงประสิทธิภาพไม่สำคัญ ดูhttps://stackoverflow.com/a/766996/1432614


1
นอกจากนี้ยังเป็นไปได้ที่จะเพิ่มค่าคงที่สตริง:COLLATE utf8_unicode_ci SET @EMAIL = 'abc@def.com' COLLATE utf8_unicode_ci;มันมีประโยชน์อย่างยิ่งหากคุณกำลังเรียกใช้สคริปต์จากคอนโซลซึ่งการเข้ารหัสเริ่มต้นของคอนโซลใช้กับการเปรียบเทียบค่าคงที่สตริงของคุณ
gaborsch

หรือวางฐานข้อมูลและสร้างใหม่ด้วย utf8_general_ci; การตรวจทาน
Oleksii Kyslytsyn

2
สำหรับการอ้างอิงในอนาคตอย่าเปลี่ยนตารางทั้งหมดเป็น utf8_general_ci เว้นแต่คุณจะเข้าใจความแตกต่างระหว่างการเปรียบเทียบสองชุด
Manatax

1
@GaborSch การเพิ่ม collate ให้กับตัวแปรสตริงเป็นวิธีแก้ปัญหาสำหรับฉันฉันเขียนคำตอบโดยละเอียดก่อนที่จะสังเกตเห็นความคิดเห็นของคุณ
nkatsar

im รับข้อผิดพลาดเดียวกันยกเว้นแทน(utf8mb4_unicode_ci, IMPLICIT) (utf8_unicode_ci, IMPLICIT)ฉันกำลังขูดข้อมูลนอกเว็บโดยใช้ไพ ธ อนจากนั้นสร้างไฟล์ CSV พร้อมข้อมูลที่ถูกคัดลอกซึ่งฉันประมวลผลด้วยไฟล์ PHP บนเซิร์ฟเวอร์ของฉันที่อัปโหลดข้อมูลไปยังฐานข้อมูลของฉัน ของฉันทั้งหมดตาราง MySQL / utf8mb4_unicode_ciคอลัมน์เรียงเป็น อาจเกิดปัญหาขึ้นเนื่องจากฉันเข้ารหัสข้อมูลเช่นเดียวกับutf8ใน python / csv
oldboy

27

ฉันใช้เวลาครึ่งวันในการค้นหาคำตอบของข้อผิดพลาด "การผสมที่ผิดพลาด" ที่ผิดพลาดพร้อมกับข้อขัดแย้งระหว่าง utf8_unicode_ci และ utf8_general_ci

ฉันพบว่าบางคอลัมน์ในฐานข้อมูลของฉันไม่ได้ตรวจทานโดยเฉพาะutf8_unicode_ci ดูเหมือนว่า MySQL เรียงโดยปริยายคอลัมน์เหล่านี้utf8_general_ci

โดยเฉพาะการเรียกใช้คิวรี 'SHOW CREATE Table table1' จะแสดงผลลัพธ์ดังนี้:

| 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) ชุดอักขระ utf8 NOT NULLไม่ได้ระบุการเปรียบเทียบ จากนั้นฉันก็เรียกใช้แบบสอบถามต่อไปนี้:

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

วิธีนี้แก้ไขข้อผิดพลาด "การผสมที่ผิดพลาด" ของฉัน หวังว่านี่อาจช่วยคนอื่นที่นั่น


7
ขอบคุณ 'SHOW CREATE TABLE' เป็นวิธีที่ง่ายที่สุดในการทำความเข้าใจและแก้ไขสาเหตุของปัญหา
joro

2
นอกจากนี้โปรดทราบว่าการระบุCOLLATEทั้งตาราง (เช่นALTER TABLE table1 CHARSET utf8 COLLATE utf8_unicode_ci) จะไม่สามารถแก้ไขปัญหาได้จะต้องดำเนินการกับแต่ละคอลัมน์ (มีปัญหา)
Skippy le Grand Gourou

6

ผมมีปัญหาที่คล้ายกัน SET @value='foo'แต่มันเกิดขึ้นกับผมภายในขั้นตอนเมื่อพระรามแบบสอบถามของฉันถูกตั้งค่าการใช้เช่นตัวแปร

สิ่งที่ทำให้เกิดสิ่งนี้คือไม่ตรงกันcollation_connectionและการเปรียบเทียบฐานข้อมูล เปลี่ยนcollation_connectionเพื่อจับคู่collation_databaseและปัญหาหายไป ฉันคิดว่านี่เป็นวิธีที่หรูหรากว่าการเพิ่ม COLLATE หลังพารามิเตอร์ / ค่า

หากต้องการสรุป: การเปรียบเทียบทั้งหมดต้องตรงกัน ใช้SHOW VARIABLESและตรวจสอบให้แน่ใจcollation_connectionและcollation_databaseจับคู่ (ตรวจสอบการเรียงตารางโดยใช้SHOW TABLE STATUS [table_name])


1
ปัญหาเดียวกันเกิดขึ้นกับฉันฉันหลีกเลี่ยงการเปลี่ยนตัวแปร collation_YYY โดยการตั้งค่าการเรียงโดยตรงในการประกาศตัวแปร SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
nkatsar

5

บิตคล้ายกับคำตอบ @bpile collation-server = utf8_general_ciกรณีของผมก็คือการตั้งค่ารายการ หลังจากที่ฉันรู้ว่า (และหลังจากลองทุกอย่างด้านบน) ฉันได้เปลี่ยนฐานข้อมูลของฉันเป็น utf8_general_ci แทน utf8_unicode_ci และนั่นคือ:

ALTER DATABASE `db` CHARACTER SET utf8 COLLATE utf8_general_ci;

1
มันแปลกที่การกำหนดค่าจะกระจายไปทั่ว การตั้งค่าเริ่มต้นทั้งหมดควรอยู่ในที่เดียวกัน
Manatax

0

ในกรณีของฉันเองฉันมีข้อผิดพลาดดังต่อไปนี้

การเปรียบเทียบที่ผิดกฎหมาย (utf8_general_ci, IMPLICIT) และ (utf8_unicode_ci, IMPLICIT) สำหรับการดำเนินการ '='

$ this-> db-> select ("users.username เป็น matric_no, CONCAT (users.surname, '', users.first_name, '', users.last_name) เป็น fullname") -> เข้าร่วม ('users', 'users .username = ห้องเรียน_students.matric_no ',' ซ้าย ') -> โดยที่ (' ห้องเรียน_students.session_id ', $ เซสชัน) -> โดยที่ (' ห้องเรียน_students.level_id ', $ ระดับ) -> ที่ไหน (' ห้องเรียน_students.dept_id ', $ dept );

หลังจากค้นหา google หลายสัปดาห์ฉันพบว่าทั้งสองฟิลด์ที่ฉันเปรียบเทียบประกอบด้วยชื่อการเรียงที่แตกต่างกัน ชื่อแรกเช่นชื่อผู้ใช้คือ utf8_general_ci ในขณะที่ชื่อที่สองคือ utf8_unicode_ci ดังนั้นฉันกลับไปที่โครงสร้างของตารางที่สองและเปลี่ยนฟิลด์ที่สอง (matric_no) เป็น utf8_general_ci และมันทำงานได้อย่างมีเสน่ห์


0

แม้จะพบคำถามจำนวนมากเกี่ยวกับปัญหาเดียวกัน ( 1 , 2 , 3 , 4 ) ฉันไม่เคยพบคำตอบที่นำการพิจารณามาพิจารณาแม้แต่ที่นี่

แม้ว่าจะมีวิธีแก้ไขปัญหาการทำงานหลายอย่างที่ได้รับมาแล้ว แต่ฉันต้องการพิจารณาประสิทธิภาพการทำงาน

แก้ไข: ขอบคุณ Manatax ที่ชี้ให้เห็นว่าตัวเลือกที่ 1 ไม่ประสบปัญหาด้านประสิทธิภาพ

โดยใช้ตัวเลือกที่ 1 และ 2อาคาเรียงวิธีการหล่อสามารถนำไปสู่การคอขวดที่อาจเกิดขึ้นสาเหตุที่ดัชนีใด ๆ ที่กำหนดไว้ในคอลัมน์นี้จะไม่ถูกนำมาใช้ก่อให้เกิดการสแกนแบบเต็ม

แม้ว่าฉันไม่ได้ลองใช้ตัวเลือก 3แต่ลางสังหรณ์ของฉันก็คือมันจะได้รับผลที่ตามมาเหมือนกันจากตัวเลือกที่1 และ 2

สุดท้ายตัวเลือก 4เป็นตัวเลือกที่ดีที่สุดสำหรับตารางที่มีขนาดใหญ่มากเมื่อมันทำงานได้ ฉันหมายถึงไม่มีการใช้งานอื่นที่ขึ้นอยู่กับการเปรียบเทียบเดิม

พิจารณาแบบสอบถามที่เรียบง่ายนี้:

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

ในตัวอย่างดั้งเดิมของฉันฉันมีการเข้าร่วมจำนวนมาก แน่นอน table1 และ table2 มีการเปรียบเทียบที่แตกต่างกัน การใช้ตัวดำเนินการเรียงชุดเพื่อส่งมันจะนำไปสู่การไม่ใช้ดัชนี

ดูคำอธิบาย sql ในภาพด้านล่าง

Visual Query คำอธิบายเมื่อใช้การโยน COLLATE

ในทางตรงกันข้ามตัวเลือกที่ 4สามารถใช้ประโยชน์จากดัชนีที่เป็นไปได้และนำไปสู่การสืบค้นที่รวดเร็ว

ในภาพด้านล่างคุณจะเห็นว่ามีการเรียกใช้คิวรีแบบเดียวกันหลังจากใช้ตัวเลือก 4หรือการเปลี่ยนการเรียงสคีมา / ตาราง / คอลัมน์

Visual Query คำอธิบายหลังจากการเปรียบเทียบเรียงมีการเปลี่ยนแปลงและดังนั้นจึงไม่มีการทอดเรียง

โดยสรุปหากผลการดำเนินงานที่มีความสำคัญและคุณสามารถปรับเปลี่ยนการเปรียบเทียบของตารางไปสำหรับทางเลือกที่ 4 หากคุณต้องดำเนินการกับคอลัมน์เดียวคุณสามารถใช้สิ่งนี้:

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

ขอบคุณสำหรับการสนับสนุนของคุณ Raffaele แต่ฉันเชื่อว่าตัวเลือกที่ 1 จะใช้ดัชนีเพราะคุณไม่ได้คัดเลือกตาราง แต่เป็นค่าเปรียบเทียบก่อนที่คุณจะส่งผ่านไปยัง SP
Manatax

ขอบคุณสำหรับการชี้ให้เห็นว่า มันเป็นความผิดพลาดของฉัน ฉันแก้ไขคำตอบของฉัน
Raffaele

0

สิ่งนี้เกิดขึ้นเมื่อคอลัมน์ถูกตั้งค่าอย่างชัดเจนเป็นการเปรียบเทียบที่แตกต่างกันหรือการเปรียบเทียบเริ่มต้นแตกต่างกันในตารางที่สอบถาม

หากคุณมีหลายตารางที่คุณต้องการเปลี่ยนการเรียงเมื่อเรียกใช้แบบสอบถามนี้:

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;

สิ่งนี้จะแสดงผลแบบสอบถามที่จำเป็นในการแปลงตารางทั้งหมดเพื่อใช้การเปรียบเทียบที่ถูกต้องต่อคอลัมน์


นอกจากนี้ยังเกิดขึ้นเมื่อ (ในกรณีของฉัน) การเปรียบเทียบค่าเริ่มต้นของคุณสำหรับ SP แตกต่างจากการเปรียบเทียบที่ใช้สำหรับตารางที่สอบถาม
Manatax
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.