Усунення помилок "Незаконне поєднання порівнянь" в mysql


210

Я отримую помилку нижче, коли намагаюся зробити вибір через збережену процедуру в MySQL.

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

Будь-яка ідея про те, що тут може піти не так?

Збір таблиці - це latin1_general_ciта стовпця стовпця в пункті, де знаходиться latin1_general_cs.


2
Я використовую різноманітні бази даних протягом великих періодів (починаючи з 1990 року), і використання коерцибільності зіставлення, зробленого NySQL, видається "божевільним", бази даних вирішують проблеми, накладаючи набір символів "ONE" для бази даних, тоді до процедури імпорту / експорту для перетворення з / в унікальний набір символів, що використовується в базі даних. Вибрані Mysql рішення є руйнівним, оскільки це змішування "проблем із додатком" (перетворення набору символів) з проблемою бази даних (використання зіставлення). Чому б не "видалити" з глузду та громіздких особливостей з бази даних, щоб вона стала набагато більш корисною та керованою користувачем
Мауріціо П'євайолі

Відповіді:


216

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

Стаття COLLATEдозволяє вказати зіставлення, яке використовується в запиті.

Наприклад, у наступному WHEREпункті завжди з’явиться помилка, яку ви опублікували:

WHERE 'A' COLLATE latin1_general_ci = 'A' COLLATE latin1_general_cs

Ваше рішення - вказати спільне порівняння для двох стовпців у запиті. Ось приклад, який використовує COLLATEпункт:

SELECT * FROM table ORDER BY key COLLATE latin1_general_ci;

Іншим варіантом є використання BINARYоператора:

BINARY str - це скорочення для CAST (str AS BINARY).

Ваше рішення може виглядати приблизно так:

SELECT * FROM table WHERE BINARY a = BINARY b;

або,

SELECT * FROM table ORDER BY BINARY a;

2
Дякую. Насправді, здається, він веде себе досить дивно в моєму випадку. Коли я запускаю запит таким, яким він є, через браузер запитів, він отримує мені результати. Але використання збереженої процедури видає помилку.
користувач355562

5
Бінарний здавався найкращим рішенням для мене. Це може бути найкращим для вас, якщо ви не використовуєте жодних складних фільтрів.
Адам Ф

У мене є те саме питання, як я вирішую цю проблему, відтворюю з самого початку. Я спробував змінити порівняння, але коли я приєднався, все-таки отримав помилку, тому спробував так. cmiiw
Bobby Z

Зверніть увагу, що в MariaDB є помилка, за допомогою COLLATE latin1_general_ci якої виникає ще одна помилка: COLLATION 'utf8_general_ci' is not valid for CHARACTER SET 'latin1''- навіть якщо у вас немає стовпця з CHARACTER SET 'latin1'! Рішення полягає у використанні BINARY ролях. Дивіться також це запитання
Mel_T

154

TL; DR

Або змініть зіставлення одного (або обох) рядків таким чином, щоб вони відповідали, або ж додайте додаток COLLATEдо виразу.


  1. Що це за штука «зіставлення»?

    Як задокументовано в розділі Набори персонажів і посилання загалом :

    Набір символів - це набір символів та кодувань. Звірка являє собою набір правил для порівняння символів в наборі символів. Зробимо чітке розмежування на прикладі уявного набору символів.

    Припустимо, у нас є алфавіт з чотирма літерами: " A", " B", " a", " b". Кожній букві ми даємо число: " A" = 0, " B" = 1, " a" = 2, " b" = 3. Буква " A" є символом, число 0 - кодуванням для " A", а комбінація всіх чотири літери та їх кодування - це набір символів .

    Припустимо, ми хочемо порівняти два значення рядка " A" і " B". Найпростіший спосіб зробити це - подивитися кодування: 0 для " A" і 1 для " B". Оскільки 0 менше 1, ми кажемо, що " A" менше, ніж " B". Щойно ми зробили, це застосувати порівняння до набору символів. Порівняння - це набір правил (у цьому випадку лише одне правило): "порівняйте кодування". Цей найпростіший з усіх можливих зіставлень ми називаємо двійковим порівнянням.

    Але що робити, якщо ми хочемо сказати, що малі та великі літери рівнозначні? Тоді ми мали б принаймні два правила: (1) трактувати малі літери " a" і " b" як еквівалентні " A" і " B"; (2) потім порівняйте кодування. Ми називаємо це співставленням, що не враховує регістр . Це трохи складніше, ніж двійкове порівняння.

    У реальному житті більшість наборів символів має багато символів: не лише " A" і " B", але цілі алфавіти, іноді кілька алфавітів або східні системи письма з тисячами символів, а також багато спеціальних символів та розділових знаків. Також у реальному житті у більшості посилань є багато правил, не тільки щодо того, чи слід розрізняти буквений лист, але й щодо того, чи слід розрізняти наголоси ("наголос" - знак, прикріплений до символу, як у німецькій " Ö"), і для багатозначних символів відображення (наприклад, правило " Ö" = " OE" в одному з двох німецьких зіставлень).

    Подальші приклади наводяться в Прикладах ефекту зіставлення .

  2. Гаразд, але як MySQL вирішує, яке зіставлення використовувати для заданого виразу?

    Як задокументовано у статті Збір виразів :

    У переважній більшості висловлювань очевидно, яке зіставлення використовує MySQL для вирішення операції порівняння. Наприклад, у наступних випадках має бути зрозуміло, що порівняння - це зіставлення стовпця charset_name:

    SELECT x FROM T ORDER BY x;
    SELECT x FROM T WHERE x = x;
    SELECT DISTINCT x FROM T;

    Однак з кількома операндами може бути неоднозначність. Наприклад:

    SELECT x FROM T WHERE x = 'Y';

    Чи має порівняння використовувати зіставлення стовпця xчи рядкового літералу 'Y'? Обидва xі 'Y'мають параметри сортування, так що звірка має перевагу?

    Стандартний SQL вирішує подібні питання, використовуючи правила, що називаються "примусовими".

    [ делеція ]

    MySQL використовує значення примусовості з наступними правилами для вирішення неясностей:

    • Використовуйте порівняння з найменшим значенням примусовості.

    • Якщо обидві сторони мають однакову примусовість, то:

      • Якщо обидві сторони є Unicode або обидві сторони не є Unicode, це помилка.

      • Якщо одна зі сторін має набір символів Unicode, а інша сторона має набір символів Unicode, сторона з набором символів Unicode виграє, і автоматична конверсія набору символів застосовується до сторони, яка не використовується Unicode. Наприклад, наступне твердження не повертає помилку:

        SELECT CONCAT(utf8_column, latin1_column) FROM t1;

        Він повертає результат, який має набір символів utf8та таке ж зіставлення, як і utf8_column. Значення " latin1_columnавтоматично" перетворюються до utf8об'єднання перед об'єднанням.

      • Для операції з операндами з одного і того ж набору символів, але які змішують _binзіставлення і a _ciабо _csзіставлення, використовується _binпорівняння. Це схоже на те, як операції, що змішують небінарні та бінарні рядки, оцінюють операнди як бінарні рядки, за винятком того, що це стосується порівнянь, а не типів даних.

  3. Отже, що таке "незаконна суміш зіставлень"?

    "Незаконне поєднання зіставлень" виникає, коли вираз порівнює два рядки різних зіставлень, але однакової примусовості і правила примусовості не можуть допомогти вирішити конфлікт. Це ситуація, описана під третьою позначкою у наведеній вище цитаті.

    Конкретна помилка, наведена у запитанні, Illegal mix of collations (latin1_general_cs,IMPLICIT) and (latin1_general_ci,IMPLICIT) for operation '='говорить про те, що було проведено порівняння рівності між двома рядками Unicode однакової примусовості. Крім того, це говорить нам про те, що порівняння не були викладені явно у виписці, а скоріше випливали з джерел рядків (наприклад, метаданих стовпців).

  4. Це все дуже добре, але як можна вирішити такі помилки?

    Як випливає з цитованих вище ручних витягів, цю проблему можна вирішити кількома способами, з яких два є розумними і рекомендуються:

    • Змініть зіставлення однієї (або обох) рядків так, щоб вони збігалися і більше не було двозначності.

      Як це можна зробити, залежить від того, звідки надходить рядок: Літеральні вирази приймають зіставлення, вказане в collation_connectionсистемній змінній; Значення з таблиць приймають порівняння, вказане в метаданих їх стовпців.

    • Примушуйте одну струну не бути примусовою.

      Я опустив наступну цитату із сказаного:

      MySQL призначає значення примусовості таким чином:

      • Явне COLLATEзастереження має примусовість 0. (Не примусово).

      • Сполучення двох рядків з різними зіставленнями має примусовість 1.

      • Порівнювання стовпця або збереженого параметри рутини або локальної змінної має придатність 2.

      • «Система константа» (рядок, що повертається такими функціями, як USER()або VERSION()), має придатність 3.

      • Співставлення літералу має примусовість 4.

      • NULLабо вираз, який є похідним, NULLмає примушуваність 5.

      Таким чином, просто додавання COLLATEпункту до одного з рядків, використаних для порівняння, змусить використовувати це порівняння.

    Хоча для інших буде страшно погана практика, якби вони були розгорнуті лише для усунення цієї помилки:

    • Примушуйте одну (або обидві) струни мати якесь інше значення примусовості, так що одна має перевагу.

      Використання CONCAT()або CONCAT_WS()призвело б до рядка з придатністю 1; та (якщо у збереженій програмі) використання параметрів / локальних змінних призведе до рядків з придатністю 2.

    • Змініть кодування одного (або обох) рядків так, щоб один був Unicode, а другий - ні.

      Це можна зробити за допомогою перекодування за допомогою ; або за допомогою зміни базового набору символів даних (наприклад, зміна стовпця, зміна буквальних значень або надсилання їх від клієнта в іншому кодуванні та зміна / додавання ввідника набору символів). Зверніть увагу, що зміна кодування призведе до інших проблем, якщо деякі бажані символи не можуть бути закодовані в новому наборі символів.CONVERT(expr USING transcoding_name)character_set_connectioncharacter_set_client

    • Змініть кодування одного (або обох) рядків так, щоб вони були однаковими, і змініть одну рядок, щоб використовувати відповідне _binпорівняння.

      Методи зміни кодувань і порівнянь були детально описані вище. Цей підхід буде мало корисного, якщо потрібно насправді застосувати більш досконалі правила порівняння, ніж пропонується в _binпорівнянні.


4
Зауважте, що "незаконна суміш зіставлень" також може виникати тоді, коли немає неоднозначності щодо того, яке зіставлення слід використовувати, але рядок, який повинен бути примушений, повинен бути перекодований до кодування, в якому деякі його символи не можуть бути представлені. Я обговорював цей випадок у попередній відповіді .
eggyal

5
Чудова відповідь. Це має бути далі, оскільки воно заглиблюється у те, що розробники повинні насправді знати; не просто як це виправити, а реально розуміти, чому все відбувається так, як вони;
позначте

Дякую чувак, ти сьогодні мене чомусь навчив.
briankip

66

Додаю 2с до дискусії для майбутніх гуглерів.

Я досліджував подібну проблему, коли я отримав таку помилку під час використання спеціальних функцій, які отримували параметр varchar:

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

Використовуючи наступний запит:

mysql> show variables like "collation_database";
    +--------------------+-----------------+
    | Variable_name      | Value           |
    +--------------------+-----------------+
    | collation_database | utf8_general_ci |
    +--------------------+-----------------+

Я зміг сказати, що БД використовує utf8_general_ci , тоді як таблиці були визначені за допомогою utf8_unicode_ci :

mysql> show table status;
    +--------------+-----------------+
    | Name         | Collation       |
    +--------------+-----------------+
    | my_view      | NULL            |
    | my_table     | utf8_unicode_ci |
    ...

Зауважте, що перегляди мають зіставлення NULL . Здається, що в представленнях і функціях є визначення зіставлення, хоча цей запит показує нуль для одного представлення даних. Використовуване порівняння - це зіставлення БД, яке було визначене під час створення представлення / функції.

Сумним рішенням було як змінити зіставлення db, так і відтворити погляди / функції, щоб змусити їх використовувати поточне порівняння.

  • Зміна зіставлення db:

    ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;
  • Зміна складання таблиці:

    ALTER TABLE mydb CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Я сподіваюся, що це комусь допоможе.


12
Порівняння може також встановлюватися на рівні стовпця. Переглянути його можна за допомогою:show full columns from my_table;
Джонатан Тран

Дякую. Я просто скинув схему і заново створив її з правильним зіставленням за замовчуванням і повторно імпортував усе.
JRun

1
@JonathanTran Дякую! У мене було встановлено набір символів та порівняння для всіх таблиць, бази даних та з'єднання, але воно все-таки створювало помилку! Порівняння не було встановлено для стовпця! Я зафіксував цеalter table <TABLE> modify column <COL> varchar(255) collate utf8_general_ci;
Хлоя

2
Мітка для майбутніх googlers: Навіть якщо у вашій базі даних, таблицях і полях є однакове порівняння, ви також повинні переконатися, що для вашого з'єднання використовується той самий порівняння. У всіх є "utf8mb4_unicode_ci", але SHOW session variables like '%collation%';говорить вам, що "collation_connection" - це "utf8mb4_general_ci"? Потім запустити SET collation_connection = utf8mb4_unicode_ciзаздалегідь.
піксельні брекети

Дякую! Знадобився час, щоб відстежити це. Мало того, що таблиці мають бути однаковими, але і БД!
мото

15

Інколи може бути небезпечно перетворювати шаблони, особливо на бази даних з величезною кількістю даних. Я вважаю, що найкращим варіантом є використання «двійкового» оператора:

e.g : WHERE binary table1.column1 = binary table2.column1

10

У мене була схожа проблема, намагався використовувати процедуру FIND_IN_SET зі змінною рядка .

SET @my_var = 'string1,string2';
SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);

і отримував помилку

Код помилки: 1267. Незаконне поєднання порівнянь (utf8_unicode_ci, IMPLICIT) та (utf8_general_ci, IMPLICIT) для операції 'find_in_set'

Коротка відповідь:

Не потрібно змінювати жодних змінних collation_YYYY, просто додайте правильне порівняння поруч із вашим оголошенням змінної , тобто

SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);

Довга відповідь:

Я спершу перевірив змінні зіставлення:

mysql> SHOW VARIABLES LIKE 'collation%';
    +----------------------+-----------------+
    | Variable_name        | Value           |
    +----------------------+-----------------+
    | collation_connection | utf8_general_ci |
    +----------------------+-----------------+
    | collation_database   | utf8_general_ci |
    +----------------------+-----------------+
    | collation_server     | utf8_general_ci |
    +----------------------+-----------------+

Потім я перевірив порівняння таблиці:

mysql> SHOW CREATE TABLE my_table;

CREATE TABLE `my_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `column_name` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=125 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Це означає, що моя змінна була налаштована за замовчуванням порівняння utf8_general_ci, тоді як моя таблиця була налаштована як utf8_unicode_ci .

Додавши команду COLLATE поруч із декларацією змінної, змінне зіставлення збігається із зіставленням, налаштованим для таблиці.



2

Рішення, якщо задіяні літерали.

Я використовую інтеграцію даних Pentaho і не можу вказати синтаксис sql. Використання дуже простого пошуку БД дало помилку "Незаконне поєднання порівнянь (cp850_general_ci, COERCIBLE) та (latin1_swedish_ci, COERCIBLE) для операції '='"

Згенерований код був "ВИБІР DATA_DATE AS last_DATA_DATE FROM hr_cc_normalised_data_date_v WHERE PSEUDO_KEY =?"

Якщо скоротити історію, короткий пошук перейшов до перегляду, і коли я видав

mysql> show full columns from hr_cc_normalised_data_date_v;
+------------+------------+-------------------+------+-----+
| Field      | Type       | Collation         | Null | Key |
+------------+------------+-------------------+------+-----+
| PSEUDO_KEY | varchar(1) | cp850_general_ci  | NO   |     |
| DATA_DATE  | varchar(8) | latin1_general_cs | YES  |     |
+------------+------------+-------------------+------+-----+

що пояснює, звідки походить 'cp850_general_ci'.

Представлення було просто створено за допомогою "SELECT" X ", ......" Відповідно до цього посібника літерали повинні успадковувати свій набір символів та зіставлення з налаштувань сервера, які були правильно визначені як "latin1" та "latin1_general_cs". явно не сталося, я змусив це створити погляд

CREATE OR REPLACE VIEW hr_cc_normalised_data_date_v AS
SELECT convert('X' using latin1) COLLATE latin1_general_cs        AS PSEUDO_KEY
    ,  DATA_DATE
FROM HR_COSTCENTRE_NORMALISED_mV
LIMIT 1;

тепер він показує latin1_general_cs для обох стовпців і помилка зникла. :)


1

MySQL дійсно не любить змішування зіставлень, якщо тільки він не може примусити їх до того ж (що, очевидно, у Вашому випадку неможливо). Ви не можете просто змусити використовувати одне і те ж порівняння за допомогою пункту COLLATE ? (або більш простий BINARYярлик, якщо застосовано ...).


Це унікально для MySQL? Як інші системи обробляють суміш несумісних зіставлень, очевидно, однакового пріоритету?
eggyal

Ваше посилання недійсне.
Benubird

1

Якщо стовпці, з якими виникають проблеми, є "хешами", то врахуйте наступне ...

Якщо "хеш" - це двійковий рядок, ви дійсно повинні використовувати BINARY(...)тип даних.

Якщо "хеш" - це шістнадцятковий рядок, вам не потрібен utf8, і слід уникати такого через перевірку символів тощо. Наприклад, MySQL MD5(...)дає шістнадцяткову 32-байтну шістнадцяткову рядок. SHA1(...)дає 40-байтовий шістнадцятковий рядок Це може бути збережено в CHAR(32) CHARACTER SET ascii(або 40 для sha1).

Або, ще краще, зберігати UNHEX(MD5(...))в BINARY(16). Це скорочує вдвічі розмір стовпця. (Однак це робить його досить недрукувальним.), SELECT HEX(hash) ...Якщо ви хочете, щоб він був читабельним.

Якщо порівнювати два BINARYстовпці, немає проблем зіставлення.


1

Дуже цікаво ... Тепер будьте готові. Я переглянув усі рішення "додати порівнювати", і мені це - виправлення дозволу на групи. Реальність полягає в тому, що дизайн бази даних був "поганим". Так, стандартні зміни та нові речі додаються, бла-бла, але це не змінює поганий факт дизайну бази даних. Я відмовляюся йти маршрутом додавання "зіставлення" у всіх операторах SQL лише для того, щоб мій запит працював. Єдине рішення, яке працює для мене і фактично позбавить від необхідності налаштувати мій код у майбутньому, - це переробити базу даних / таблиці, щоб відповідати набору символів, з якими я буду жити, і охоплювати його в довгостроковому майбутньому. У цьому випадку я вирішую перейти з набором символів " utf8mb4 ".

Тож рішення тут, коли ви стикаєтесь з тим, що "незаконним" повідомленням про помилку, є перепроектування вашої бази даних та таблиць. Це набагато простіше і швидше, ніж це звучить. Експорт даних та повторний імпорт їх із CSV може навіть не знадобитися. Змініть набір символів бази даних і переконайтеся, що весь набір символів ваших таблиць відповідає.

Скористайтеся цими командами, щоб навести вас:

SHOW VARIABLES LIKE "collation_database";
SHOW TABLE STATUS;

Тепер, якщо вам подобається додавати "порівнювати" туди-сюди і збільшити свій код з усіма силами "переопределення", будьте здогадним.



0

Ще одне джерело питання із порівняннями - mysql.procтаблиця. Перевірте порівняння процедур та функцій зберігання:

SELECT
  p.db, p.db_collation, p.type, COUNT(*) cnt
FROM mysql.proc p
GROUP BY p.db, p.db_collation, p.type;

Також зверніть увагу mysql.proc.collation_connectionі на mysql.proc.character_set_clientколонки.


0

Якщо у вас встановлено phpMyAdmin, ви можете слідувати вказівкам, наведеним за наступним посиланням: https://mediatemple.net/community/products/dv/204403914/default-mysql-character-set-and-collation Ви повинні відповідати порівнянню бази даних, що містить усі таблиці, а також поля таблиць, а потім перекомпілюйте всі збережені процедури та функції. З цим все має працювати знову.


-1

Я звик ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;, але не працював.

У цьому запиті:

Select * from table1, table2 where table1.field = date_format(table2.field,'%H');

Ця робота для мене:

Select * from table1, table2 where concat(table1.field) = date_format(table2.field,'%H');

Так, тільки a concat.


Перевірте зіставлення ваших таблиць та їх стовпців (покажіть стан таблиці; показуйте повні стовпці з таблиці1;) Використання бази даних alter не буде працювати, якщо таблиці вже створені з неправильним зіставленням.
Аріель Т

АЛЬТЕР ДАТАБАЗА mydb DEFAULT COLLATE ... працював на мене, тому підняв нагоду. Можливо, я мав перевагу, оскільки міг скинути та відтворити базу даних та завантажити з резервних копій.
tobixen

-2

Цей код потрібно помістити всередині запуску SQL запитів / запитів у базі даних

SQL QUERY WINDOW

ALTER TABLE `table_name` CHANGE `column_name` `column_name`   VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL;

Будь ласка, замініть ім’я таблиці та стовпця на відповідне ім’я.

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