Niedozwolona kombinacja zestawień (utf8_unicode_ci, IMPLICIT) i (utf8_general_ci, IMPLICIT) dla operacji „=”

160

Komunikat o błędzie w MySql:

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

Przeszedłem przez kilka innych postów i nie byłem w stanie rozwiązać tego problemu. Część, której to dotyczy, jest podobna do tej:

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;

Procedura składowana, której używam, jest następująca:

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

Testowałem z php, ale ten sam błąd pojawia się w SQLyog. Przetestowałem również odtworzenie całego DB, ale bez rezultatu.

Każda pomoc będzie mile widziana.

Manatax
źródło

Odpowiedzi:

220

Domyślne sortowanie parametrów procedury składowanej to utf8_general_cii nie można mieszać sortowania, więc masz cztery opcje:

Opcja 1 : dodaj COLLATEdo zmiennej wejściowej:

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

Opcja 2 : dodaj COLLATEdo WHEREklauzuli:

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

Opcja 3 : dodaj ją do INdefinicji parametru:

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

Opcja 4 : zmień samo pole:

ALTER TABLE users CHARACTER SET utf8 COLLATE utf8_general_ci;

O ile nie musisz sortować danych w kolejności Unicode, sugerowałbym zmianę wszystkich tabel w celu użycia utf8_general_cisortowania, ponieważ nie wymaga to zmian w kodzie i nieznacznie przyspieszy sortowanie.

UPDATE : utf8mb4 / utf8mb4_unicode_ci jest teraz preferowanym zestawem znaków / metodą sortowania. utf8_general_ci jest odradzany, ponieważ poprawa wydajności jest znikoma. Zobacz https://stackoverflow.com/a/766996/1432614

Ross Smith II
źródło
1
Możliwe jest również dodanie COLLATE utf8_unicode_cido stałych łańcuchowych: SET @EMAIL = '[email protected]' COLLATE utf8_unicode_ci;. Jest to szczególnie przydatne, jeśli uruchamiasz skrypt z konsoli, gdzie domyślne kodowanie konsoli ma zastosowanie do sortowania stałych łańcuchowych.
gaborsch
Lub upuść bazę danych i utwórz nową za pomocą utf8_general_ci; porównanie.
Oleksii Kyslytsyn
2
Na przyszłość nie zmieniaj wszystkich swoich tabel na utf8_general_ci, chyba że rozumiesz różnice między tymi dwoma zestawieniami.
Manatax,
1
@GaborSch Dodanie sortowania do zmiennych łańcuchowych było dla mnie rozwiązaniem, napisałem szczegółową odpowiedź na ten temat, zanim zauważyłem Twój komentarz.
nkatsar
otrzymuję ten sam błąd, z wyjątkiem (utf8mb4_unicode_ci, IMPLICIT)zamiast (utf8_unicode_ci, IMPLICIT). Zbieram dane z sieci za pomocą języka Python, a następnie tworzę plik CSV ze skrobanymi danymi, który następnie przetwarzam za pomocą pliku PHP na serwerze, który przesyła dane do mojej bazy danych. wszystkie moje tabele / kolumny MySQL są sortowane jako utf8mb4_unicode_ci. Czy problem może wynikać z tego, że koduję dane jak utf8w python / csv?
oldboy,
27

Spędziłem pół dnia na poszukiwaniu odpowiedzi na identyczny błąd „Niedozwolona mieszanka zestawień” z konfliktami między utf8_unicode_ci i utf8_general_ci.

Zauważyłem, że niektóre kolumny w mojej bazie danych nie zostały specjalnie zestawione utf8_unicode_ci . Wygląda na to, że mysql niejawnie zestawił te kolumny utf8_general_ci .

Konkretnie, uruchomienie zapytania „SHOW CREATE TABLE table1” zwróciło coś takiego:

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

Zwróć uwagę, że wiersz „col1” varchar (4) CHARACTER SET utf8 NOT NULL nie ma określonej kolacji. Następnie uruchomiłem następujące zapytanie:

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

To rozwiązało mój błąd „Nielegalna mieszanka zestawień”. Mam nadzieję, że to pomoże komuś innemu.

Nate Vaughan
źródło
7
dzięki. „POKAŻ STWÓRZ TABELĘ” to najłatwiejszy sposób zrozumienia i usunięcia pierwotnej przyczyny problemu.
joro
2
Zauważ również, że określenie COLLATEdla całej tabeli (tj. ALTER TABLE table1 CHARSET utf8 COLLATE utf8_unicode_ci) Nie rozwiąże problemu , trzeba to zrobić dla każdej (problematycznej) kolumny.
Skippy le Grand Gourou
6

Miałem podobny problem, ale przyszło mi do głowy wewnątrz procedury, kiedy mój parametr zapytania został ustawiony za pomocą zmiennej np SET @value='foo'.

Przyczyną tego było niedopasowanie collation_connectioni sortowanie w bazie danych. Zmieniono collation_connectionna dopasowanie collation_databasei problem zniknął. Myślę, że jest to bardziej eleganckie podejście niż dodawanie Sortuj po parametrze / wartości.

Podsumowując: wszystkie zestawienia muszą się zgadzać. Użyj SHOW VARIABLESi upewnij się collation_connectioni collation_databasedopasuj (sprawdź także sortowanie tabeli za pomocą SHOW TABLE STATUS [table_name]).

bpile
źródło
1
Zdarzyło mi się to samo, uniknąłem zmiany zmiennych collation_YYY, ustawiając sortowanie bezpośrednio w deklaracji zmiennej. SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
nkatsar
5

Trochę podobnie do odpowiedzi @bpile, w moim przypadku było ustawienie wpisu my.cnf collation-server = utf8_general_ci. Po tym, jak zdałem sobie z tego sprawę (i po wypróbowaniu wszystkiego powyżej), siłą przełączyłem moją bazę danych na utf8_general_ci zamiast utf8_unicode_ci i to wszystko:

ALTER DATABASE `db` CHARACTER SET utf8 COLLATE utf8_general_ci;
Sebas
źródło
1
To dziwne, że konfiguracje są tak rozproszone. Wszystkie domyślne ustawienia sortowania należy ustawić w tym samym miejscu.
Manatax,
0

W moim przypadku mam następujący błąd

Niedozwolona kombinacja zestawień (utf8_general_ci, IMPLICIT) i (utf8_unicode_ci, IMPLICIT) dla operacji „=”

$ this-> db-> select ("users.username as matric_no, CONCAT (user.first_name, '', 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 );

Po tygodniach wyszukiwania w Google zauważyłem, że dwa porównywane przeze mnie pola mają różne nazwy zestawień. Pierwsza, tj. Nazwa użytkownika, to utf8_general_ci, podczas gdy druga to utf8_unicode_ci, więc wróciłem do struktury drugiej tabeli i zmieniłem drugie pole (matric_no) na utf8_general_ci i działało jak urok.

Teejaygenius
źródło
0

Pomimo znalezienia ogromnej liczby pytań dotyczących tego samego problemu ( 1 , 2 , 3 , 4 ) nigdy nie znalazłem odpowiedzi, która uwzględniłaby wydajność, nawet tutaj.

Chociaż podano już wiele działających rozwiązań, chciałbym rozważyć wydajność.

EDYCJA: Podziękowania dla Manatax za wskazanie, że opcja 1 nie ma problemów z wydajnością.

Korzystanie z opcji 1 i 2 , czyli metody rzutowania COLLATE , może prowadzić do potencjalnego wąskiego gardła, ponieważ żaden indeks zdefiniowany w kolumnie nie zostanie użyty, co spowoduje pełne skanowanie .

Mimo że nie wypróbowałem opcji 3 , mam przeczucie, że będzie ona miała takie same konsekwencje, jak opcja 1 i 2.

Wreszcie opcja 4 jest najlepszą opcją w przypadku bardzo dużych stołów, gdy jest to wykonalne. Chodzi mi o to, że nie ma innych zastosowań, które opierają się na oryginalnym zestawieniu.

Rozważ to uproszczone zapytanie:

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

W moim oryginalnym przykładzie miałem znacznie więcej połączeń. Oczywiście tabela1 i tabela2 mają różne sortowania. Użycie operatora sortowania do rzutowania spowoduje, że indeksy nie będą używane.

Zobacz wyjaśnienie sql na poniższym obrazku.

Wizualne wyjaśnienie zapytania podczas korzystania z rzutowania COLLATE

Z drugiej strony opcja 4 może skorzystać z możliwego indeksu i prowadzić do szybkich zapytań.

Na poniższym obrazku widać, że to samo zapytanie jest uruchamiane po zastosowaniu opcji 4 , czyli po zmianie schematu / tabeli / sortowania kolumn.

Wizualne wyjaśnienie zapytania po zmianie sortowania, a zatem bez rzutowania sortowania

Podsumowując, jeśli wydajność jest ważna i możesz zmienić sortowanie tabeli, wybierz opcję 4 . Jeśli musisz działać na jednej kolumnie, możesz użyć czegoś takiego:

ALTER TABLE schema1.table1 MODIFY `field` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Raffaele
źródło
Dziękuję za Twój wkład Raffaele, ale wierzę, że opcja 1 użyłaby indeksu, ponieważ nie rzucasz tabeli, ale wartość porównawczą, zanim jeszcze przekażesz ją do SP.
Manatax,
Dzięki za zwrócenie uwagi. To był mój błąd. Odpowiednio zredagowałem odpowiedź.
Raffaele,
0

Dzieje się tak, gdy kolumna jest jawnie ustawiona na inne sortowanie lub domyślne sortowanie jest inne w tabeli, której dotyczy zapytanie.

jeśli masz wiele tabel, w których chcesz zmienić sortowanie po uruchomieniu tego zapytania:

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;

Spowoduje to wyświetlenie zapytań potrzebnych do przekonwertowania wszystkich tabel w celu użycia prawidłowego sortowania na kolumnę

raam86
źródło
Dzieje się tak również wtedy, gdy (tak jak w moim przypadku) Twoje domyślne sortowanie dla SP jest inne niż sortowanie używane dla zapytanej tabeli.
Manatax,