SQL не различает u и ü, хотя сопоставление utf8mb4_unicode_ci

В таблице x есть столбец со значениями u и ü.

SELECT * FROM x WHERE column='u'.

Это возвращает u И ü, хотя я ищу только u.

Сортировка таблицы utf8mb4_unicode_ci . Где бы я ни читал о подобных проблемах, все предлагают использовать эту сортировку, потому что говорят, что utf8mb4 действительно покрывает ВСЕ СИМВОЛЫ. При таком сопоставлении должны быть решены все проблемы с набором символов и сопоставлением.

Я могу вставить ü, è, é, à, Chinese characters и т. д. Когда я делаю SELECT *, они также извлекаются и отображаются правильно.

Проблема возникает только тогда, когда я СРАВНИВАЮ две строки, как в приведенном выше примере (SELECT WHERE), или когда я использую UNIQUE INDEX в столбце. Когда я использую UNIQUE INDEX, "ü" не вставляется, когда у меня уже есть "u" в столбце. Итак, когда SQL сравнивает u и ü, чтобы решить, является ли ü уникальной, он считает, что это то же самое, что и u, и не вставляет ü.

Я изменил все на utf8mb4, потому что не хочу больше беспокоиться о наборах символов и сопоставлении. Однако кажется, что utf8mb4 тоже не является решением, когда дело доходит до СРАВНЕНИЯ строк.

Я также пробовал это: SELECT * FROM x WHERE _utf8mb4 'ü' COLLATE utf8mb4_unicode_ci = column.
Этот код является исполняемым (выглядит довольно сложным). Однако он также возвращает ü И u.

Я разговаривал с некоторыми людьми в Индии и здесь, в Китае, по этому вопросу. Мы еще не нашли решения.

Если бы кто-нибудь смог разгадать тайну, было бы действительно здорово.

Add_On: прочитав все ответы и комментарии ниже, вот пример кода, который решает проблему:

SELECT * FROM x WHERE 'ü' COLLATE utf8mb4_bin = column

Добавляя «COLLATE utf8mb4_bin» в запрос SELECT, SQL предлагает надевать «бинарные очки» (оканчивающиеся на _bin) при просмотре символов в столбце. С включенными бинарными очками SQL теперь видит двоичный код в столбце. И двоичный код отличается для каждой буквы, символа и смайлика, которые только можно придумать. Итак, SQL теперь также может видеть разницу между u и ü. Таким образом, теперь он возвращает только ü, когда запрос SELECT ищет ü и не возвращает u.

Таким образом, можно оставить все (сопоставление базы данных, сопоставление таблиц) без изменений, но добавить «COLLATE utf8mb4_bin» к запросу только тогда, когда требуется точное дифференцирование.

(На самом деле, SQL снимает все остальные очки (utf8mb4_german_ci, _general_ci, _unicode_ci и т. д.) и делает то, что он делает, только когда его не заставляют делать что-либо дополнительно. Он просто смотрит на двоичный код и не подстраивает свой поиск под какие-либо особый культурный фон.)

Спасибо всем за поддержку, особенно Pred.


person Jakob    schedule 12.12.2016    source источник
comment
Вы можете прочитать dev.mysql.com/doc /refman/5.7/en/charset-unicode-sets.html   -  person Mark Rotteveel    schedule 12.12.2016
comment
Судя по вашему описанию, вы действительно хотите игнорировать сопоставление и выполнить бинарное сопоставление. Каковы ваши конкретные правила? Например, следует ли считать u и U равными или разными?   -  person Álvaro González    schedule 12.12.2016
comment
Привет Альваро. В прошлом я провел много времени со stackoverflow. Тем не менее, я никогда не публиковал вопрос. Ответы подавляющие. Stackoverflow кажется дорогой к решениям. Лучше всего: во время сравнений (WHERE или UNIQUE) все, что отображается по-разному в строках, также различается во время сравнения. Итак, ä НЕ является а. a - это НЕ A. è - это НЕ e и т. д. Если двоичный код различает все это, вероятно, это будет правильный путь. Могу ли я просто различать во время запроса и оставить все остальное без изменений? Что было бы практическим способом справиться с этим?   -  person Jakob    schedule 13.12.2016
comment
Привет Альваро. Вы также спрашиваете о правилах: 1. Это должно быть предсказуемое решение, которое Я полностью ПОНИМАЮ. 2. Если возможно, он должен работать во многих различных средах. 3. Если возможно, это должно быть простое решение. 4. По возможности должно быть быстро (сейчас это не так важно). Тем не менее, самое главное: это должно быть ПОЛНОСТЬЮ ПРЕДСКАЗУЕМЫМ и легким для ПОНИМАНИЯ и ОБРАЩЕНИЯ.   -  person Jakob    schedule 13.12.2016
comment
Ну... Дело не в том, что носители английского языка - единственная культура в истории цивилизации, которая придумала, как сортировать и искать слова в словаре, а все остальные просто делают это случайным образом...   -  person Álvaro González    schedule 13.12.2016
comment
Спасибо, Альваро. После прочтения того, что Pred написал ниже, я также понимаю ваш ответ. Да, в основе всего этого лежит то, что пользователь ожидает из-за его культурного происхождения. Спасибо.   -  person Jakob    schedule 13.12.2016
comment
Если вы хотите сворачивание регистра, но с учетом ударения, отправьте запрос по адресу bugs.mysql.com .   -  person Rick James    schedule 15.03.2017


Ответы (4)


Сопоставление и набор символов - две разные вещи.

Набор символов — это просто «неупорядоченный» список символов и их представление. utf8mb4 — это набор символов, который охватывает множество символов.

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

В utf8mb4_unicode_ci все (большинство?) символы с акцентом рассматриваются как один и тот же символ, поэтому вы получаете u и ü. Короче говоря, эта сортировка нечувствительна к акценту.

Это похоже на тот факт, что немецкие сопоставления обрабатывают ss и ß как одно и то же.

utf8mb4_bin — это еще одно сопоставление, и оно рассматривает все символы как разные. Вы можете или не хотите использовать его по умолчанию, это зависит от вас и ваших бизнес-правил.

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

Вот пример использования похожей, но, возможно, более знакомой части сопоставления:

ci в конце сопоставления означает Case Insensitive, и почти все сопоставления с ci имеют пару, заканчивающуюся на cs, что означает Case Sensitive.

Если ваш столбец нечувствителен к регистру, условие where column = 'foo' найдет все это: foo Foo fOo FoO Foo FoO fOO, FOO.

Теперь, если вы попытаетесь установить сортировку с учетом регистра (например, utf8mb4_unicode_cs), все приведенные выше значения будут рассматриваться как разные значения.

Локализованные сопоставления (например, немецкий, британский, американский, венгерский и т. д.) следуют правилам именованного языка. В Германии ss и ß одинаковы, и это указано в правилах немецкого языка. Когда немецкий пользователь ищет значение Straße, он будет ожидать, что программное обеспечение (поддерживающее немецкий язык или написанное в Германии) вернет как Straße, так и Strasse.

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

Не забывайте, что ограничение UNIQUE — это всего лишь способ упорядочивания/фильтрации значений. Таким образом, если в столбце с немецким сопоставлением определен уникальный ключ, он не позволит вставить как Straße, так и Strasse, поскольку по правилам языка они должны рассматриваться как равные.

Теперь давайте посмотрим на нашу исходную сортировку: utf8mb4_unicode_ci. Это «универсальная» сортировка, что означает, что она пытается упростить все, поэтому, поскольку ü не очень распространенный символ, и большинство пользователей понятия не имеют, как его вводить, эта сортировка делает он равен u. Это упрощение для поддержки большинства языков, но, как вы уже знаете, такого рода упрощения имеют некоторые побочные эффекты. (например, при упорядочении, фильтрации, использовании уникальных ограничений и т. д.).

utf8mb4_bin — это другой конец спектра. Это сопоставление разработано, чтобы быть настолько строгим, насколько это возможно. Для этого он буквально использует коды символов для различения символов. Это означает, что каждая форма символа отличается, это сопоставление неявно чувствительно к регистру и ударению.

У обоих есть недостатки: локализованные и общие сопоставления предназначены для одного конкретного языка или для предоставления общего решения. (utf8mb4_unicode_ci является «расширением» старого сопоставления utf8_general_ci)

Двоичный файл требует особой осторожности, когда дело доходит до взаимодействия с пользователем. Поскольку это CS и AS, это может сбить с толку пользователей, которые привыкли получать значение «Foo», когда они ищут значение «foo». Кроме того, как разработчик, вы должны быть особенно осторожны, когда речь идет о объединениях и других функциях. ВНУТРЕННЕЕ СОЕДИНЕНИЕ 'foo' = 'Foo' ничего не вернет, так как 'foo' не равно 'Foo'.

Я надеюсь, что эти примеры и объяснения немного помогут.

person Pred    schedule 12.12.2016
comment
Привет Пред. Большое спасибо, особенно за ОБЪЯСНЕНИЕ, что есть что. Я потратил недели своей жизни на проблемы с character_set и сопоставлением, а также на обходные пути. Теперь я нахожусь в той точке, когда хочу действительно ПОНЯТЬ, что происходит, и найти решение, которое работает, по крайней мере, в большинстве случаев, ТОЧНО ЗНАЯ, в каких случаях оно не работает, И ПОНИМАЯ, ПОЧЕМУ. До сих пор я не знал, что сопоставление также определяет, какие символы ОБРАБАТЫВАЮТСЯ КАК ОДИНАКОВЫЕ ВО ВРЕМЯ СРАВНЕНИЯ, хотя сопоставление также определяет, что они ОТОБРАЖАЮТСЯ ПО-РАЗНОМУ. Это разрывает мой логический ум на части. - person Jakob; 13.12.2016
comment
utf8mb4_bin РАБОТАЕТ. Спасибо. Однако я не понимаю, почему, и я не понимаю недостатков этого решения. Поскольку я не знаю всего этого, я не знаю, как интегрировать это в запрос из php и т. Д. Итак, пришло время еще немного почитать о bin. Вы мне уже очень помогли. СПАСИБО. - person Jakob; 13.12.2016
comment
@Jakob Обновил ответ с примерами и еще много чего, надеюсь, это поможет :) - person Pred; 13.12.2016
comment
Спасибо за этот отличный отличный ответ! Вы не только объясняете, но и добавляете примеры, которые позволяют легко поместить ваши объяснения в правильный контекст. Теперь я вижу. В основе всего этого лежит взаимодействие с пользователем. Немецкий пользователь хочет, чтобы ß и ss обрабатывались одинаково. И французский пользователь может захотеть, чтобы a и à обрабатывались одинаково, поскольку он не всегда может быть уверен, добавил ли он акцент или нет. Если программист не использует локализованную сортировку, он должен позаботиться обо всех этих ожиданиях пользователей, изменив свой код. Я понял. Окончательно. Ух ты. Я ПОНИМАЮ. - person Jakob; 13.12.2016
comment
Добро пожаловать, и я желаю счастливого кодирования. Я бы предложил сначала поиграть с этими примерами и различными сопоставлениями. И самое главное: Понимание потребностей вашего бизнеса, это поможет выбрать правильную сортировку. Например. если вы пишете программное обеспечение только для немецких или французских пользователей, вы можете использовать локализованные сопоставления. Общий подходит для большинства случаев использования, он устраняет множество возможных недоразумений, рассматривая множество символов как одинаковые. С двоичным кодом сложно работать, он имеет множество ограничений, которые могут потребовать дополнительных обходных путей в коде приложения. - person Pred; 13.12.2016
comment
И: если НЕТ ВЗАИМОДЕЙСТВИЯ С ПОЛЬЗОВАТЕЛЕМ и я хочу, чтобы система делала именно то, что я ей говорю, я должен использовать двоичную сортировку (utf8mb4_bin). Тогда каждая крошечная деталь будет рассматриваться как отдельная. Наконец то я понял. БЛАГОДАРНОСТЬ! - person Jakob; 13.12.2016
comment
Нет системы без взаимодействия с пользователем :) Да, если вы хотите представить все как есть, вы можете использовать параметры сортировки *_bin. Имейте в виду, что это означает, что вам также придется иметь дело с чувствительностью к регистру, когда речь идет о соединениях, фильтрации и упорядочении. - person Pred; 13.12.2016
comment
Например, если вы хотите соединить две таблицы на основе ключа символа, ключ должен быть одинаковым в обеих таблицах, включая диакритические знаки и буквы нижнего/верхнего регистра: FOO != Foo. Или, если вы хотите сделать это без учета регистра, вам придется преобразовать обе стороны (LOWER('FOO') = LOWER('Foo')). Второй означает, что MySQL не будет использовать индексы, так как он должен применить преобразование, которое делает индексы бесполезными. - person Pred; 13.12.2016
comment
Спасибо за эти примеры. Примеры отличные. Решением было бы настроить все на нижний регистр, прежде чем вставлять его в столбец INDEX, а затем при поиске строки подготовить строку вне mysql (например: изменить все на LOWERCASE), прежде чем искать ее в столбце индекса. Теперь я получаю полную картину. Также недостатки бинарника. - person Jakob; 13.12.2016
comment
Я пришел сюда, ЧТОБЫ НАПИСАТЬ ЗАГЛАВНЫМИ БУКВАМИ в некоторых словах, ПОТОМУ ЧТО, ВОЗМОЖНО, MARKDOWN не поддерживался ЧЕТЫРЕ ГОДА НАЗАД, но теперь мы можем ИСПОЛЬЗОВАТЬ MARKDOWN для выделения. :) - person Lemmings19; 03.10.2020

utf8_collations.html перечисляет, какие буквы "равны" в различных сопоставления utf8 (или utf8mb4). За редким исключением все диакритические знаки удаляются перед сравнением в любом ..._ci сопоставлении. Некоторые исключения зависят от языка, а не от Unicode в целом. Пример: в исландском É > E.

..._bin — это единственная сортировка, которая учитывает буквы с акцентом как разные. То же самое для складывания корпуса.

Если вы часто сравниваете, вам следует изменить порядок сортировки столбца на ..._bin. При использовании предложения COLLATE в WHERE нельзя использовать индекс.

Примечание к ß. ss = ß практически во всех сопоставлениях. В частности, utf8_general_ci (который раньше был значением по умолчанию) рассматривал их как неравные. Этот один вариант сортировки не пытался рассматривать любую комбинацию из двух букв (ss) как одну "букву". Кроме того, из-за ошибки в версии 5.0 utf8_general_mysql500_ci относится к ним неодинаково.

В дальнейшем utf8mb4_unicode_520_ci является лучшим до версии 5.7. Для 8.0 utf8mb4_0900_ai_ci "лучше". «520» и «900» относятся к стандартам Unicode, поэтому в будущем могут появиться еще более новые.

person Rick James    schedule 13.12.2016

Вы можете попробовать сопоставление utf8_bin, и вы не должны столкнуться с этой проблемой, но она будет чувствительна к регистру. Сопоставления бинов строго сравниваются, только разделяя символы в соответствии с выбранной кодировкой, и как только это сделано, сравнения выполняются на двоичной основе, так же, как многие языки программирования сравнивают строки.

person Community    schedule 12.12.2016
comment
Привет Вир. Вы пишете: ... только разделяя символы в соответствии с выбранной кодировкой,... . Можете ли вы привести пример выбранной кодировки? Вы имеете в виду uft8 или utf8mb4? - person Jakob; 13.12.2016
comment
Попробуйте использовать SELECT * FROM x WHERE 'ü' COLLATE utf8_bin = столбец. Дайте мне знать, как это работает. - person ; 13.12.2016
comment
Привет Вир. Широкая улыбка сейчас. Это именно то, что я сделал только что. Я сделал дополнение к моему вопросу выше. Да, это работает. - person Jakob; 13.12.2016
comment
Рад это знать. Продолжайте качаться! - person ; 13.12.2016
comment
@jakob Еще одно обновление по этому поводу от MySQL: mysqlserverteam.com/ - person ; 31.01.2017

Я просто добавлю к другим ответам, что сопоставление _bin также имеет свои особенности.

Например, после следующего:

CREATE TABLE `dummy` (`key` VARCHAR(255) NOT NULL UNIQUE);
INSERT INTO `dummy` (`key`) VALUES ('one');

это не удастся:

INSERT INTO `dummy` (`key`) VALUES ('one ');

Это описано в разделе бинарные сопоставления по сравнению с сопоставлениями _bin< /а>.

Изменить: я разместил связанный вопрос здесь.

person Sea Coast of Tibet    schedule 21.02.2017