Незаконне поєднання порівнянь (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зіставлення, оскільки це не потребує змін коду, і це буде швидко сортувати.

ОНОВЛЕННЯ : 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; співставлення
Олексій Кислицин

2
Для подальшого ознайомлення не змінюйте всі таблиці на utf8_general_ci, якщо ви не розумієте відмінності між двома посиланнями.
Манатакс

1
@GaborSch Додавання колатера до змінних рядків було рішенням для мене, я написав докладну відповідь про це, перш ніж помітив ваш коментар.
nkatsar

я отримую ту ж помилку, за винятком (utf8mb4_unicode_ci, IMPLICIT)замість (utf8_unicode_ci, IMPLICIT). Я знімаю дані з Інтернету за допомогою python, потім створюю 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) CHARACTER SET 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, у моєму випадку було налаштування запису my.cnf collation-server = utf8_general_ci. Після того як я зрозумів, що (і спробувавши все вище), я насильно переключив свою базу даних на utf8_general_ci замість utf8_unicode_ci, і це було:

ALTER DATABASE `db` CHARACTER SET utf8 COLLATE utf8_general_ci;

1
Дивно, що конфігурації так розкинуті навколо. Усі параметри за замовчуванням повинні бути встановлені на одному місці.
Манатакс

0

У моєму випадку у мене є така помилка

Незаконне поєднання порівнянь (utf8_general_ci, IMPLICIT) та (utf8_unicode_ci, IMPLICIT) для операції '='

$ this-> db-> select ("users.username як matric_no, CONCAT (user.surname, '', users.first_name, '', users.last_name) як повне ім'я") -> приєднатися ('users', 'users .username = classroom_students.matric_no ',' зліва ') -> де (' classroom_students.session_id ', $ сесія) -> де (' classroom_students.level_id ', $ level) -> де (' classroom_students.dept_id ', $ dept );

Після тижнів пошуку Google я помітив, що два поля, які я порівнюю, складаються з різних назв зіставлення. Перше, тобто ім'я користувача, має utf8_general_ci, тоді як друге - utf8_unicode_ci, тому я повернувся до структури другої таблиці і змінив друге поле (matric_no) на utf8_general_ci, і воно спрацювало як шарм.


0

Незважаючи на те, що я знайшов величезну кількість запитань щодо тієї самої проблеми ( 1 , 2 , 3 , 4 ), я жодного разу не знайшов відповіді, яка б враховувала ефективність, навіть тут.

Хоча вже було розроблено кілька робочих рішень, я хотів би розглянути результативність.

EDIT: Дякуємо Manatax, що вказав, що варіант 1 не страждає від ефективності.

Використання Варіантів 1 та 2 , також підхід до відтворення COLLATE , може призвести до потенційного вузького місця, оскільки будь-який індекс, визначений у стовпці, не буде використовуватися, викликаючи повне сканування .

Навіть незважаючи на те, що я не спробував варіант 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')
;

У своєму оригінальному прикладі я мав ще багато приєднань. Звичайно, таблиця1 та table2 мають різні порівняння. Використовуючи оператор зіставлення для передачі, це призведе до того, що індекси не будуть використані.

Дивіться пояснення sql на малюнку нижче.

Візуальний запит Пояснення при використанні команди COLLATE

З іншого боку, варіант 4 може скористатися можливими показниками та призведе до швидких запитів.

На малюнку нижче ви можете побачити той самий запит, який виконується після застосованого Варіанту 4 , який також змінює порівняння схеми / таблиці / стовпців.

Візуальний запит Пояснення після того, як порівняння було змінено, а отже, без відтворення колатера

На закінчення, якщо продуктивність важлива і ви можете змінити складання таблиці, перейдіть до варіанту 4 . Якщо вам доведеться діяти в одному стовпчику, ви можете використовувати щось подібне:

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

Дякую за Ваш внесок Raffaele, але я вважаю, що варіант 1 використовує індекс, оскільки ви не кидаєте таблицю, а порівнюєте значення перед тим, як навіть передати її SP.
Манатакс

Дякуємо, що вказали на це. Це була моя помилка. Я відповідно відредагував свою відповідь.
Раффаеле

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 відрізняється від порівняння, яке використовується для запиту таблиці.
Манатакс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.