Как заставить MySQL знать о многобайтовых символах в LIKE и REGEXP? - PullRequest
13 голосов
/ 26 июня 2011

У меня есть таблица MySQL с двумя столбцами, оба с разбором utf8_unicode_ci. Он содержит следующие строки. За исключением ASCII, второе поле также содержит кодовые точки Unicode, такие как U + 02C8 (ВЕРТИКАЛЬНАЯ ВЕРТИКАЛЬНАЯ ПИСЬМА ИЗМЕНЕННОГО ПИСЬМА) и U + 02D0 (ТРЕУГОЛЬНЫЙ КОЛОН ИЗМЕНЕННОГО ПИСЬМА).

 word   | ipa
--------+----------
 Hallo  | haˈloː
 IPA    | ˌiːpeːˈʔaː

Мне нужно найти второе поле с помощью LIKE и REGEXP, но MySQL (5.0.77), по-видимому, интерпретирует эти поля как байты, а не как символы.

SELECT * FROM pronunciation WHERE ipa LIKE '%ha?lo%';  -- 0 rows
SELECT * FROM pronunciation WHERE ipa LIKE '%ha??lo%'; -- 1 row

SELECT * FROM pronunciation WHERE ipa REGEXP 'ha.lo';  -- 0 rows
SELECT * FROM pronunciation WHERE ipa REGEXP 'ha..lo'; -- 1 row

Я совершенно уверен, что данные хранятся правильно, так как, когда я их получаю, они кажутся хорошими и хорошо отображаются в phpMyAdmin. Я на общем хосте, поэтому я не могу устанавливать программы.

Как я могу решить эту проблему? Если это невозможно: есть ли правдоподобный обходной путь, который не требует обработки всей базы данных с помощью PHP каждый раз? Есть 40 000 строк, и я не намерен использовать MySQL (или UTF8, если на то пошло). У меня есть доступ только к PHP и MySQL на хосте.

Редактировать: Существует открытый 4-летний отчет об ошибке MySQL, Ошибка # 30241 Проблемы с регулярными выражениями , в котором отмечается, что механизм регулярных выражений работает побайтово. Таким образом, я ищу обходной путь.

Ответы [ 3 ]

9 голосов
/ 28 июня 2011

ИЗМЕНЕНО для включения исправления к действительной критике

Используйте функцию HEX(), чтобы преобразовать ваши байты в шестнадцатеричное значение и , затем используйте RLIKE для этого, дляпример:

select * from mytable
where hex(ipa) rlike concat('(..)*', hex('needle'), '(..)*'); -- looking for 'needle' in haystack, but maintaining hex-pair alignment.

Нечетные символы Юникода отображаются в соответствии с их шестнадцатеричными значениями, поэтому вы ищете по стандартным символам 0-9A-F.

Это работает и для "обычных" столбцоввам это просто не нужно.

ps @ (действительный) пункт Киерена, адресованный с помощью rlike для принудительного использования пар символов

3 голосов
/ 28 июня 2011

Я не настроен использовать MySQL

Postgres, кажется, справляется с этим довольно хорошо:

test=# select 'ˌˈʔ' like '___';
 ?column? 
----------
 t
(1 row)

test=# select 'ˌˈʔ' ~ '^.{3}$';
 ?column? 
----------
 t
(1 row)

Если вы пойдете по этому пути, обратите внимание, что в Postgres оператор ilike совпадает с оператором MySQL like. (В Postgres like чувствителен к регистру.)


Для решения, специфичного для MySQL, вы можете обойтись путем связывания некоторой пользовательской функции (возможно, связать ICU library ?) С MySQL.

0 голосов
/ 05 июля 2011

У вас проблемы с UTF8? Уничтожь их.

Сколько специальных символов вы используете? Вы используете только буквы локали, я прав? Итак, мой совет: напишите функцию, которая преобразует специальные символы в обычные, например «æ» -> «A» и т. д. и добавьте в таблицу столбец, в котором хранится это преобразованное значение (сначала необходимо преобразовать все значения, а также после каждой вставки / обновления). При поиске вам просто нужно преобразовать строку поиска с той же функцией и использовать ее в этом поле с регулярным выражением.

Если специальных символов слишком много, вам следует преобразовать их в мульти-символ. 1. Старайтесь не находить «aa» в последовательности «ba ab», используйте какой-нибудь префикс, например «@ ba @ ab». 2. Старайтесь не находить "@a" в "@ab", используя токены фиксированной длины, скажем, 2.

...