Внимание: MySQL считает, что буквы «ё» и «е» — одно и то же.
Увы, MySQL, если выбрать кодировку Unicode, считает, что «е» и «ё» — это одно и то же. Багом это не считается, в официальной таблице, где описано как MySQL 6.0 сравнивает символы, эти символы в одном ряду.
mysql> set names utf8 collate utf8_unicode_ci;
Query OK, 0 rows affected (0.00 sec)
mysql> select 'е'='ё', 'ё'='ѐ'\G
*************************** 1. row ***************************
'е'='ё': 1
'ё'='ѐ': 1
1 row in set (0.00 sec)
Самое печальное, это нелогичное поведение всплывает, если есть потребность перевести базу с CP1251 на UTF-8, так как в CP1251-то как раз всё хорошо:
mysql> set names cp1251;
Query OK, 0 rows affected (0.00 sec)
mysql> select 'е'='ё', 'ё'='ѐ'\G
*************************** 1. row ***************************
'е'='ё': 0
'ё'='ѐ': 0
1 row in set (0.00 sec)
Чем это может быть плохо? Например, у нас по какому-то текстовому полю имеется уникальный ключ. Для иллюстративности, пусть это будет словарь фамилий. Фамилии Рублев и Рублёв — разные, но MySQL так не считает (для простоты опускаю другие поля):
mysql> set names utf8 collate utf8_unicode_ci;
Query OK, 0 rows affected (0.00 sec)
mysql> create table surname(surname varchar(255) primary key);
Query OK, 0 rows affected (0.07 sec)
mysql> insert into surname values('Рублёв');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> insert into surname values('Рублев');
ERROR 1062 (23000): Duplicate entry 'Рублев' for key 'PRIMARY'
Я придумал только один способ, исправляющий это поведение (но у него есть недостаток):
mysql> create table surname(surname varchar(255) primary key collate 'utf8_bin');
Query OK, 0 rows affected (0.02 sec)
mysql> insert into surname values('Рублёв');
Query OK, 1 row affected (0.00 sec)
mysql> insert into surname values('Рублев');
Query OK, 1 row affected (0.00 sec)
mysql> select * from surname where surname='рублев';
Empty set (0.00 sec)
Я выключил case insensitive у данного столбца, явным указанием «collate». Это полностью решает проблему с «ё», но лишает нас удовольствия искать, не заботясь о совпадении регистра.
Интересно. А что вернёт
select * from surnames where LOWER(surname) = LOWER(’Рублёв’) ?
Кстати, заметил баг — если имя не вводить, то выдаст ошибку «введите имя или адрес блога» , но текст комментария пропадёт. Неприятно.
Комментарий для dinoel:
В каких условиях?
Да, я знаю, в TODO записан, но никак не пойдут руки поправить :( Но я поправлю.
create table surname(surname varchar(255) primary key collate ’utf8_bin’);
вот в таких, т. е. при бинарном сравнении.
Хотя я догадываюсь :)
Проверил, работает как надо. Т. е. проблема с регистром решается просто
select * from surname where LOWER(surname)=LOWER(’рублев’);
+--------------+
| surname |
+--------------+
| Рублев |
+--------------+
1 row in set (0.00 sec)
mysql> select * from surname where LOWER(surname)=LOWER(’рублёв’);
+--------------+
| surname |
+--------------+
| Рублёв |
+--------------+
Другое дело что с сортировкой будут проблемы..
Комментарий для dinoel:
С сортировкой и использованием индекса.
Я считаю, что подобное обращение с пользовательскими данными со стороны СУБД есть мерзость и грех великий. Кто ей разрешал брать на себя такую ответственность? Её дело — данные хранить.
А также сортировать, сравнивать и искать.
Комментарий для besisland.name:
Путать «е» и «ё» — точно великий грех. Это неудобно. В конце концов, кроме помощника кроссвордиста существует ещё множество других задач, которые хочется писать с использованием этой БД.
mysql поступает очень правильно. У «ё» давно нет прав, и не mysql их у неё отобрал, потакать желаниям считанных пуристов — это усложнять жизнь миллионам обычных людей, которые ищут и заносят в базы данных «Рублев» и «Ежик в тумане».
Дело не в «е» и «ё», а в незапрашиваемых явно операциях над строками вообще.
Комментарий для dinoel:
До сих пор из того факта, что MySQL по умолчанию сравнивает строки регистронезависимо или же приравнивает «ü» и «u», я выносил для себя только широкий спектр проблем, которые приходилось так или иначе решать.
BINARY() ?
Комментарий для kurapov.name:
Не понял.
Хм... Полезное однако иследование... Хотя, если честно, то ё редко где можно увидеть, все давно уже используют е заместо ё... Но опять таки с точки зрения русского языка правильнее будет именно ё...
Комментарий для Дмитрий (http://www.kains.ru):
Например, в фамилиях и географических названиях нельзя без «ё».
Решил проблему по примеру описаному здесь: http://www.sql.ru/forum/766045-2/besit-zhenyok-kodirovki-cp1251-utf8-bukva-yo?mid=11663963#11663963
А именно в /usr/share/mysql/charsets/Index.xml изменил
<charset name=«utf8»>
<family>Unicode</family>
<description>UTF-8 Unicode</description>
<alias>utf-8</alias>
<collation name=«utf8_general_ci» id=«33»>
<flag>primary</flag>
<flag>compiled</flag>
</collation>
<collation name=«utf8_bin» id=«83»>
<flag>binary</flag>
<flag>compiled</flag>
</collation>
<collation name=«utf8_russian_ci» id=«250»>
<rules>
<reset>\u0415</reset><p>\u0401</p> <!-- ЕЁ -->
<reset>\u0435</reset><p>\u0451</p> <!-- её -->
</rules>
</collation>
</charset>
и добавил:
<charset name=«utf8mb4»>
<family>Unicode</family>
<description>UTF-8 Unicode</description>
<alias>utf-8</alias>
<collation name=«utf8mb4_russian_ci» id=«251»>
<rules>
<reset>\u0415</reset><p>\u0401</p> <!-- ЕЁ -->
<reset>\u0435</reset><p>\u0451</p> <!-- её -->
</rules>
</collation>
</charset>
после чего выполнил:
ALTER TABLE `таблица` CHANGE `столбец` `столбец` тип CHARACTER SET utf8mb4 COLLATE utf8mb4_russian_ci NOT NULL DEFAULT ’’;