Недопустимое сочетание параметров сортировки (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. Я также протестировал воссоздание всей базы данных, но безрезультатно.

Любая помощь будет высоко ценится.

Manatax
источник

Ответы:

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;

Если вам не нужно сортировать данные в порядке Юникода, я бы предложил изменить все ваши таблицы для использования utf8_general_ciсортировки, поскольку это не требует изменений кода и немного ускорит сортировку.

ОБНОВЛЕНИЕ : utf8mb4 / utf8mb4_unicode_ci теперь является предпочтительным методом набора символов / сопоставления. utf8_general_ci не рекомендуется, так как улучшение производительности незначительно. См. Https://stackoverflow.com/a/766996/1432614.

Росс Смит II
источник
1
Также можно добавить COLLATE utf8_unicode_ciк строковым константам: SET @EMAIL = 'abc@def.com' COLLATE utf8_unicode_ci;. Это особенно полезно, если вы запускаете скрипт из консоли, где кодировка консоли по умолчанию применяется к сопоставлению ваших строковых констант.
gaborsch
Или удалите базу данных и создайте новую с помощью utf8_general_ci; сверка.
Алексей Кислицын
2
Для дальнейшего использования не изменяйте все свои таблицы на utf8_general_ci, если вы не понимаете различия между этими двумя параметрами сортировки.
Манатакс
1
@GaborSch Решением для меня было добавление сортировки в строковые переменные, я написал подробный ответ, прежде чем заметил ваш комментарий.
нкацар
Я получаю ту же ошибку, кроме (utf8mb4_unicode_ci, IMPLICIT)вместо (utf8_unicode_ci, IMPLICIT). Я удаляю данные из Интернета с помощью python, затем создаю CSV-файл со соскребенными данными, который затем обрабатываю с помощью файла PHP на моем сервере, который загружает данные в мою базу данных. все мои таблицы / столбцы MySQL сопоставлены как utf8mb4_unicode_ci. Может ли проблема возникнуть, потому что я кодирую данные, как utf8в Python / CSV?
старик
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) не решит проблему , это должно быть сделано для каждого (проблемного) столбца.
Скиппи ле Гран Гуру
6

У меня была похожая проблема, но она возникла у меня внутри процедуры, когда мой параметр запроса был установлен с помощью переменной, например SET @value='foo'.

Причиной этого было несоответствие collation_connectionи сопоставление базы данных. Изменился, collation_connectionчтобы соответствовать, collation_databaseи проблема ушла. Я думаю, что это более элегантный подход, чем добавление COLLATE после параметра / значения.

Подводя итог: все сопоставления должны совпадать. Используйте SHOW VARIABLESи убедитесь collation_connectionи collation_databaseсопоставьте (также проверьте сопоставление таблицы, используя SHOW TABLE STATUS [table_name]).

bpile
источник
1
Со мной произошла та же проблема, я избежал изменения переменных collation_YYY, установив параметры сортировки непосредственно в объявлении переменной. SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
нкацар
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;
Sebas
источник
1
Странно, что конфигурации так сильно распространены. Все параметры сортировки по умолчанию должны быть установлены в одном месте.
Манатакс
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) как полное имя") -> join ('users', 'users .username = classroom_students.matric_no ',' left ') -> где (' classroom_students.session_id ', $ session) -> где (' classroom_students.level_id ', $ level) -> где (' classroom_students.dept_id ', $ dept );

После нескольких недель поиска в Google я заметил, что два поля, которые я сравниваю, состоят из разных имен сопоставлений. Первый, то есть имя пользователя, имеет utf8_general_ci, а второй - utf8_unicode_ci, поэтому я вернулся к структуре второй таблицы и изменил второе поле (matric_no) на utf8_general_ci, и оно работало как шарм.

Teejaygenius
источник
0

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

Хотя уже было дано несколько рабочих решений, я хотел бы рассмотреть вопрос о производительности.

РЕДАКТИРОВАТЬ: Спасибо 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')
;

В моем исходном примере у меня было еще много соединений. Конечно, table1 и table2 имеют разные параметры сортировки. Использование оператора collate для приведения приводит к тому, что индексы не используются.

См. Объяснение sql на картинке ниже.

Визуальное объяснение запроса при использовании приведения COLLATE

С другой стороны, вариант 4 может использовать преимущества возможного индекса и приводит к быстрым запросам.

На рисунке ниже вы можете увидеть тот же запрос, который выполняется после применения варианта 4 , то есть, изменения схемы / таблицы / столбца.

Визуальное объяснение запроса после изменения параметров сортировки и, следовательно, без преобразования

В заключение, если производительность важна и вы можете изменить параметры сортировки таблицы, перейдите к варианту 4 . Если вам нужно действовать на один столбец, вы можете использовать что-то вроде этого:

ALTER TABLE schema1.table1 MODIFY `field` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Раффаэль
источник
Спасибо за ваш вклад, Раффаэле, но я верю, что вариант 1 будет использовать индекс, потому что вы не приводите таблицу, а сравниваете значение еще до того, как передадите ее в SP.
Manatax
Спасибо что подметил это. Это была моя ошибка. Я отредактировал свой ответ соответственно.
Раффаэле
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;

это выведет запросы, необходимые для преобразования всех таблиц, чтобы использовать правильное сопоставление для столбца

raam86
источник
Это также происходит, когда (как в моем случае) ваше сопоставление по умолчанию для SP отличается от сопоставления, используемого для запрашиваемой таблицы.
Manatax