Как да конвертирам колона в ASCII в движение, без да записвам, за да проверя за съвпадения с външен ASCII низ?

Имам функция за търсене на членове, където можете да дадете части от имена и връщането трябва да бъде всички членове, които имат поне едно от потребителско име, собствено име или фамилия, съответстващи на този вход. Проблемът тук е, че някои имена имат „странни“ символи като é в Renée и потребителят не иска да въведе странния знак, а нормалния ASCII заместител e.

В PHP конвертирам входния низ в ASCII с помощта на iconv (само в случай, че някой въведе странни знаци). В базата данни обаче трябва също да конвертирам странните знаци в ASCII (очевидно), за да съвпадат низовете.

Опитах следното:

SELECT
  CONVERT(_latin1'Renée' USING ascii) t1, 
  CAST(_latin1'Renée' AS CHAR CHARACTER SET ASCII) t2;

(Това са два опита.) И двата не работят. И двете имат Ren?e като изход. Въпросителният знак трябва да бъде e. Добре е, ако извежда Ren?ee, тъй като мога просто да премахна всички въпросителни след преобразуването.

Както можете да си представите, колоните, които искам да запитам, са кодирани Latin1.

Благодаря.


person Rudie    schedule 20.11.2010    source източник
comment
Възможно ли е това по друг начин? Твърде много свобода е добре, просто ще филтрирам резултатите с PHP стриктно след филтъра в MySQL. напр. Добър съм с получаването на Renee, Renée и Renäe и Renõe като MySQL резултати при въвеждане на Renee. Така или иначе??   -  person Rudie    schedule 22.11.2010
comment
Най-добрият начин би бил да съхранявате кибритите. Преобразуването в движение е изключително бавно за огромни данни, защото не е интелигентно за индексиране.   -  person Pacerier    schedule 11.02.2015


Отговори (4)


Не е нужно да конвертирате нищо. Вашето изискване е да сравните два низа и да попитате дали са равни, като игнорирате ударенията; сървърът на базата данни може да използва съпоставяне, за да направи това вместо вас:

Съпоставянията, които не са UCA, имат съпоставяне едно към едно от кода на символа към теглото. В MySQL такива съпоставяния са нечувствителни към главни и малки букви и акценти. utf8_general_ci е пример: 'a', 'A', 'À' и 'á' имат различни кодове на знаци, но всички имат тегло 0x0041 и се сравняват като равни.

mysql> SET NAMES 'utf8' COLLATE 'utf8_general_ci';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT 'a' = 'A', 'a' = 'À', 'a' = 'á';
+-----------+-----------+-----------+
| 'a' = 'A' | 'a' = 'À' | 'a' = 'á' |
+-----------+-----------+-----------+
|         1 |         1 |         1 |
+-----------+-----------+-----------+
1 row in set (0.06 sec)
person Vince Bowdren    schedule 24.12.2010
comment
горното не е вярно, съхранението на данни е latin1 и OP може да не прилага UTF-8 в кодирането на страницата - person ajreal; 24.12.2010
comment
За съжаление (?) това не работи. Опитвам select id from members where lastname like 'test6e%' (със стойност за lastname от 'test6ë'). Няма върнати записи. Базата данни, таблицата и колоната са UTF8. Когато опитам select 'Reneé' = 'Renee', 'Renëe' = 'Renee'; те връщат true. Странно? - person Rudie; 24.12.2010
comment
@Rudie: Работи добре в моята среда, но погледнете отговора ми за обяснения/точности. - person Danosaure; 25.12.2010

Първо, трябва да работи по следния начин:

SELECT * FROM `test` WHERE `name` COLLATE utf8_general_ci LIKE '%renee%';

Където е таблицата test:

+-----+--------+
| id  | name   |
+-----+--------+
|  1  | Renée  |
|  2  | Renêe  |
|  3  | Renee  |
+-----+--------+

Каква е вашата версия на MySQL и как се опитвате да съпоставите нещата?


Едно от другите възможни решения е транслитерация.

Свързани: PHP транслитерация

Транслитерирането на входа не би трябвало да е проблем, но транслитерирането на стойностите от постоянното хранилище (напр. db) в реално време по време на търсенето може да не е осъществимо. Така че можете да добавите още три полета като: username_slug, firstname_slug и lastname_slug. Когато вмъквате/модифицирате запис, задайте подходящо стойностите на slug. И когато търсите, търсете транслитерирания вход срещу полетата за охлюв.

+------+----------+---------------+----------+---------------+ ...
| id   | username | username_slug | lastname | lastname_slug | ...
+------+----------+---------------+----------+---------------+ ...
|    1 | Renée    |    renee      | La Niña  | la-nina       | ...
|    2 | Renêe    |    renee      | ...      | ...           | ...
|    3 | Renee    |    renee      | ...      | ...           | ...
+------+----------+---------------+----------+---------------+ ...

Търсенето на „renee“ или „renèe“ ще съответства на всички записи.

Като страничен ефект може да можете да използвате тези полета за генериране на SEF (удобни за търсачката) връзки, поради което те се наричат ​​,..._slug, напр. example.com/users/renee. Разбира се, в този случай трябва да проверите за уникалността на полето slug.

person Halil Özgür    schedule 26.12.2010
comment
Може да е добра идея в началото, но този тип настройка е кошмар за актуализация... освен ако нямате солидна рамка и никой никога няма да актуализира директно базата данни ръчно. Наистина бих предпочел транслитерация в реално време, вместо да я пазя в базата данни. - person Danosaure; 27.12.2010
comment
Обикновено да, но денормализацията обикновено е на път, ако производителността започне да се притеснява :) - person Halil Özgür; 28.12.2010

Отговорът на @vincebowdren по-горе работи, просто добавям това като отговор за целите на форматирането:

CREATE TABLE `members` (
  `id` int(11) DEFAULT NULL,
  `lastname` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL
);
insert into members values (1, 'test6ë');
select id from members where lastname like 'test6e%';

Добиви

+------+
| id   |
+------+
|    1 |
+------+

И използвайки Latin1,

set names latin1;
CREATE TABLE `members2` (
  `id` int(11) DEFAULT NULL,
  `lastname` varchar(20) CHARACTER SET latin1 DEFAULT NULL
);
insert into members2 values (1, 'Renée');
select id from members2 where lastname like '%Renee%';

ще даде:

+------+
| id   |
+------+
|    1 |
+------+

Разбира се, OP трябва да има същия набор от символи в приложението (PHP), връзката (MySQL на Linux използва по подразбиране latin1 в 5.0, но по подразбиране е UTF8 в 5.1) и в полето тип данни, за да има по-малко неизвестни. Сравняването се грижи за останалото.

РЕДАКТИРАНЕ: Написах трябва, за да имам по-добър контрол върху всичко, но следното също работи:

set names latin1;
select id from members where lastname like 'test6ë%';

Тъй като след като наборът от знаци за връзка е зададен, MySQL извършва преобразуването вътрешно. В този случай той ще конвертира по някакъв начин и ще сравни UTF8 низа (от DB) с latin1 (от заявка).

РЕДАКТИРАНЕ 2: Известен скептицизъм изисква от мен да дам още по-убедителен пример:

Предвид горните твърдения, ето какво направих повече. Уверете се, че терминалът е в UTF8.

set names utf8;
insert into members values (5, 'Renée'), (6, 'Renêe'), (7, 'Renèe');
select members.id, members.lastname, members2.id, members2.lastname
from members inner join members2 using (lastname);

Не забравяйте, че members е в utf8, а members2 е в latin1.

+------+----------+------+----------+
| id   | lastname | id   | lastname |
+------+----------+------+----------+
|    5 | Renée    |    1 | Renée    |
|    6 | Renêe    |    1 | Renée    |
|    7 | Renèe    |    1 | Renée    |
+------+----------+------+----------+

което доказва с правилните настройки, сортирането върши работата вместо вас.

person Danosaure    schedule 25.12.2010
comment
@Danosaure - не е вярно, това, което сравнявате на Renée, е UTF8, а не latin1 (iso-8859-*) - person ajreal; 25.12.2010
comment
@ajreal: Преведено е. Тествах го преди да публикувам. Трябва да конфигурирате съответно вашата среда (терминал, връзка и сортиране). Тествахте ли го, преди да кажете, че греша? - person Danosaure; 25.12.2010
comment
@Danosaure - очевидно грешиш... твоят метод предполага, че всички знаци са в utf-8, което не е вярно. моите съвети към вас, използвайте char_length - person ajreal; 26.12.2010
comment
@ajreal: Грешно го четеш. Едната таблица е в utf8, а другата е в latin1. Моля, прочетете внимателно, преди да кажете, че предполагам нещо. Опитахте ли, преди да кажете, че греша? Използвайте терминал, а не PHP. Настройте средата си правилно, за да работи. - person Danosaure; 26.12.2010
comment
@Danasaure - въздишка, въведеното от потребителя може да бъде всичко, не се ограничава само до utf-8, кой ти казва, че е utf-8? Очевидно е iso-8859-*. И което е по-важно, няма значение как се съхранява в базата данни, важно е как входът се сравнява с базата данни - person ajreal; 26.12.2010
comment
@ajreal: Очевидно не искаш да разбереш. Моят пример използва сортиране, за да докаже, че низ utf8 се сравнява правилно с низ latin1. Очевидно низът latin1 не се нуждае от преобразуване, за да направи това, което OP трябва да направи, ако наборът от знаци за връзка е latin1. Може би не разбирате как работят сравняванията, за да спорите толкова много. Няма да продължавам тази безсмислена дискусия, ако продължавате да се фокусирате върху набора от знаци, защото не доказвате, че не работи. Грешите, защото наборът от знаци за съхранение има значение. - person Danosaure; 26.12.2010
comment
@Danosaure Сигурен съм, че вашите тестове работят, но в моята среда не е така. Опитах SET NAMES 'utf8' COLLATE 'utf8_general_ci'; и SET NAMES 'utf8';, преди да направя заявка към таблицата с членове. Входящият низ е ASCII (PHP гарантира това). Цялата база данни (колони/таблици/база данни) е UTF8. Не знам за „връзката“ (откъде да знам?). Заявката select id, lastname from members where lastname like 'test6e%'; не дава резултати. - person Rudie; 26.12.2010
comment
Проведох още няколко теста и нещо странно се случва при запазване на фамилните имена (и други колони, които предполагам) в базата данни. Когато изпълня update members set lastname = 'test6é' where id = 226948; на UTF8 страница и след това направя заявка lastname с този идентификатор, той връща 'test6é'. Когато го запазя в PhpMyAdmin (също на UTF8 страница), той записва и връща правилната стойност: 'test6é'. Какво става?? И двете страници използват една и съща MySQLi връзка и имат заглавка <meta charset=utf8> (в HTML и HTTP). (Стойностите се показват правилно в моето приложение!) - person Rudie; 26.12.2010
comment
@Rudie: Трябва да има параметър за връзка за указване на набора от знаци. За съжаление не владея PHP, а само MySQL. Просто се чудя защо в OP сте използвали latin1 и вашите тестове в UTF8? - person Danosaure; 26.12.2010
comment
@Rudie: ако искате да настроите mysqli връзката на вашия php да използва специфично кодиране (в идеалния случай utf-8), можете да използвате метода mysqli::set_charset. Вижте php.net/manual/en/mysqli.set-charset.php за подробности. Използвам това в моя php, след като открих, че без него по погрешка съм записвал лошо кодирани данни в моята база данни. - person Vince Bowdren; 26.12.2010

Операторът CAST() в контекста на кодирането на знаци превежда от един метод за съхранение на знаци в друг — той не променя действителните знаци, което е, което търсите. Знакът é е това, което е във всеки набор от знаци, той не е e. Трябва да преобразувате знаци с ударение в знаци без ударение, което е различен проблем и е задаван няколко пъти преди (нормализиране на знаци с ударение в MySQL заявки).

Не съм сигурен дали има начин да направя това директно в MySQL, освен да имам таблица за превод и да преминавам буква по буква. Най-вероятно би било по-лесно да напишете PHP скрипт, който да премине през базата данни и да направи преводите.

person Orbling    schedule 20.11.2010
comment
Не искам да запазвам „преводите“. Преводите са само за търсене. Резултатите на дисплея трябва да са такива, каквито са: с акценти и други екзотични знаци. Ако PHP има функция за него (iconv), защо не и MySQL? Не (искам) да вярвам в това! - person Rudie; 22.11.2010
comment
@Rudie PHP има много функции, които MySQL няма. SQL езиците обикновено са много леки по отношение на тяхната стандартна библиотека. Би било възможно да напишете функция, за да постигнете това, което желаете, въпреки че производителността може да не е фантастична, освен ако не сте я написали като UDF или собствена функция (въпреки че с последната можете просто да извикате библиотеката iconv). - person Orbling; 22.11.2010