2017-02-21 20 views
2

Я изучаю этот вопрос уже несколько часов, на SO, в документах MySQL и в других местах, но до сих пор не могу найти удовлетворительное решение. Проблема заключается в следующем:Как заставить MySQL обрабатывать строки, подобные SQLite, в отношении Unicode и сортировки?

Каков самый простой способ заставить MySQL обрабатывать строки так же, как SQLite, без каких-либо дополнительных «умных» преобразований?

Например, следующие прекрасно работает в SQLite:

CREATE TABLE `dummy` (`key` VARCHAR(255) NOT NULL UNIQUE); 

INSERT INTO `dummy` (`key`) VALUES ('one'); 
INSERT INTO `dummy` (`key`) VALUES ('one '); 
INSERT INTO `dummy` (`key`) VALUES ('One'); 
INSERT INTO `dummy` (`key`) VALUES ('öne'); 

SELECT * FROM `dummy`; 

Однако, в MySQL, со следующими параметрами:

[client] 
default-character-set = utf8mb4 

[mysql] 
default-character-set = utf8mb4 

[mysqld] 
character-set-client-handshake = FALSE 
character-set-server = utf8mb4 
collation-server = utf8mb4_bin 

и следующее CREATE DATABASE заявление:

CREATE DATABASE `dummydb` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_bin; 

все еще не удается на втором INSERT.

Я предпочел бы максимально упростить декларации строковых столбцов, что является идеальным идеалом SQLite TEXT. Похоже VARBINARY это путь, но я все же хотел бы услышать ваше мнение по любой другой, потенциально лучшеварианты.


Добавление: SHOW CREATE TABLE dummy выход

mysql> SHOW CREATE TABLE dummy; 
+-------+----------------------------------------------------- 
| Table | Create Table           
+-------+----------------------------------------------------- 
| dummy | CREATE TABLE `dummy` (
    `key` varchar(255) COLLATE utf8mb4_bin NOT NULL, 
    UNIQUE KEY `key` (`key`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin | 
+-------+----------------------------------------------------- 
1 row in set (0.00 sec) 
+0

Это по умолчанию. Вы переопределяете их в своем заявлении 'CREATE TABLE'? –

+0

Álvaro, какие части вы имеете в виду? Весь SQL представлен как представленный. –

+0

Версия 5.7.17. –

ответ

1

MySQL хочет преобразовать строки при выполнении INSERT и SELECT. Конверсия находится между тем, что вы заявляете клиенту , чтобы иметь и что указывается столбец.

Единственный способ избежать этого - VARBINARY и BLOB вместо VARCHAR и TEXT.

Использование COLLATION utf8mb4_bin не исключает конверсии в/из CHARACTER SET utf8mb4; он просто говорит, что WHERE и ORDER BY должны сравнивать биты вместо того, чтобы иметь дело с акцентами и складыванием футляра.

Имейте в виду, что CHARACTER SET utf8mb4 - способ кодирования текста; COLLATION utf8mb4_* - это правила для сравнения текстов в этой кодировке. _bin - простой.

UNIQUE предполагает сравнение для равенства, следовательно COLLATION. В большинстве расчетов utf8mb4 3 (без пробелов) сравниваются равными. utf8mb4_bin будет рассматривать 3 как разные. utf8mb4_hungarian_ci лечит один = один> öne.

Конечные пробелы управляются типом данных столбца (VARCHAR или другим). В последней версии даже есть параметр, относящийся к рассмотрению конечных пробелов.

+0

Не уверен, что я последую за вами, но сортировка также влияет на уникальные индексы. Так как на самом деле у него есть текст (а не двоичные данные, например, сырой хеш), его подход просто хорош (и он * должен * работать). –

+0

Спасибо за ваш ответ. Я переключился на 'VARBINARY' и вернул сортировку в' utf8mb4_unicode_ci', потому что странное было более очевидным. Да, я намерен сохранить потенциальные неявные преобразования при написании запросов. –

+0

Что касается конечных пробелов, вы имеете в виду 8.0.x «последней версией»? Я видел «PADSPACE», упомянутый в документах 5.7.x, но не как изменить его для столбца. –

1

Подход демонстрируемый в вопросе должен (в основном) прекрасно работают в MySQL по следующим причинам:

  1. Collation (не следует путать с кодированием) является набор или правила, которые определяют, как сортировать и сравнивать символы, обычно используемые для тиражирования на уровне базы данных ожиданий пользователей с точки зрения культуры (если я ищу cafe I , ожидайте, чтобы найти café).

  2. Сортировка играет важную роль в уникальных ограничениях, поскольку она устанавливает определение уникального.

  3. Двоичные сортировки специально предназначены для игнорирования культурных правил и работы на уровне байтов, поэтому utf8mb4_bin - правильный выбор здесь.

  4. MySQL позволяет установить комбинацию кодирования и сопоставления с гранулярностью на уровне столбца.

  5. Если в определении столбца отсутствует сортировка, он будет использовать таблицу уровня один.

  6. Если в определении таблицы отсутствует сортировка, он будет использовать уровень базы данных один.

  7. Если в определении базы данных отсутствует сортировка, она будет использовать серверный уровень один.

Стоит также отметить, что MySQL будет конвертировать между кодировками прозрачно до тех пор, как:

  • кодирования Подключения правильно установить
  • преобразования физически возможно (например, все символы источника также принадлежат к целевой кодировке)

По этой последней причине VARBINARY, возможно, не лучший выбор для столбца, который по-прежнему является текстовым, поскольку он ns дверь для получения café, хранящейся из соединения, настроенного для использования ISO-8859-1, и не может правильно извлечь его из соединения, настроенного для использования UTF-8.


Примечание стороны: определение таблицы показано, может вызвать следующее сообщение об ошибке:

ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes

Индексы могут иметь относительно небольшой максимальный размер. От docs:

If innodb_large_prefix is enabled (the default), the index key prefix limit is 3072 bytes for InnoDB tables that use DYNAMIC or COMPRESSED row format. If innodb_large_prefix is disabled, the index key prefix limit is 767 bytes for tables of any row format.

innodb_large_prefix is deprecated and will be removed in a future release. innodb_large_prefix was introduced in MySQL 5.5 to disable large index key prefixes for compatibility with earlier versions of InnoDB that do not support large index key prefixes.

The index key prefix length limit is 767 bytes for InnoDB tables that use the REDUNDANT or COMPACT row format. For example, you might hit this limit with a column prefix index of more than 255 characters on a TEXT or VARCHAR column, assuming a utf8mb3 character set and the maximum of 3 bytes for each character.

Attempting to use an index key prefix length that exceeds the limit returns an error. To avoid such errors in replication configurations, avoid enabling innodb_large_prefix on the master if it cannot also be enabled on slaves.

С utf8_mb8 выделяет 4 байта на символ, 767 предел будет потоплен только 192 символов.


У нас есть еще одна проблема:

mysql> CREATE TABLE `dummy` (
    -> `key` varchar(191) COLLATE utf8mb4_bin NOT NULL, 
    -> UNIQUE KEY `key` (`key`) 
    ->) 
    -> ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; 
Query OK, 0 rows affected (0.01 sec) 

mysql> INSERT INTO `dummy` (`key`) VALUES ('one'); 
Query OK, 1 row affected (0.00 sec) 

mysql> INSERT INTO `dummy` (`key`) VALUES ('one '); 
ERROR 1062 (23000): Duplicate entry 'one ' for key 'key' 

Pardon?

mysql> INSERT INTO `dummy` (`key`) VALUES ('One'); 
Query OK, 1 row affected (0.00 sec) 

mysql> INSERT INTO `dummy` (`key`) VALUES ('öne'); 
Query OK, 1 row affected (0.00 sec) 

mysql> SELECT * FROM `dummy`; 
+-----+ 
| key | 
+-----+ 
| One | 
| one | 
| öne | 
+-----+ 
3 rows in set (0.00 sec) 

Эта последняя проблема представляет интересную тонкость MySQL-сопоставлений. Из docs:

All MySQL collations are of type PADSPACE. This means that all CHAR, VARCHAR, and TEXT values in MySQL are compared without regard to any trailing spaces. “Comparison” in this context does not include the LIKE pattern-matching operator, for which trailing spaces are significant

[...] For those cases where trailing pad characters are stripped or comparisons ignore them, if a column has an index that requires unique values, inserting into the column values that differ only in number of trailing pad characters will result in a duplicate-key error.

Я бы осмелился сказать то, что VARBINARY типа это единственный способ преодолеть это ...

+0

Спасибо за подробный ответ. Чтобы уточнить: я не получаю никаких ошибок (кроме ошибки INSERT, вызванной PADSPACE, для «один »), поэтому проблемы с длиной ключа, вероятно, связаны с вашей конкретной настройкой MySQL. –

+0

@SeaCoastofTibet Проблема с размером ключа объясняется в выдержках, которые я поделил. Тем не менее, я должен признаться, что я неправильно понял ваш вопрос все время, пока прямо сейчас. Я упрямо думал, что двоичная сортировка вообще не вступает в силу, что не так. Я читал слишком быстро. Я удаляю связанные комментарии, чтобы избежать распространения дезинформации. –