4

我有一个成员搜索功能,您可以在其中提供部分名称,并且返回应该是所有成员至少具有与该输入匹配的用户名、名字或姓氏之一。这里的问题是有些名字有像éin这样的“奇怪”字符Renée,用户不想输入奇怪的字符而是普通的 ASCII 替代字符e

在 PHP 中,我使用 iconv 将输入字符串转换为 ASCII(以防有人键入奇怪的字符)。但是,在数据库中,我还应该将奇怪的字符转换为 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 编码的。

谢谢。

4

4 回答 4

7

你不需要转换任何东西。您的要求是比较两个字符串并询问它们是否相等,忽略重音;数据库服务器可以使用排序规则为您执行此操作:

非 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)
于 2010-12-24T11:33:32.153 回答
6

首先,它应该以这种方式工作:

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_slugfirstname_sluglastname_slug。插入/修改记录时,适当设置 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 字段的唯一性。

于 2010-12-26T14:11:05.113 回答
3

字符编码上下文中的CAST()运算符将字符存储的一种方法转换为另一种 - 它不会更改实际字符,这就是您所追求的。é 字符是它在任何字符集中的样子,它不是 e。您需要将重音字符转换为非重音字符,这是一个不同的问题,之前已被多次询问(规范化 MySQL 查询中的重音字符)。

我不确定是否有一种方法可以直接在 MySQL 中执行此操作,而不需要有一个翻译表并逐个字母地检查。编写一个 PHP 脚本来遍历数据库并进行翻译很可能会更容易。

于 2010-11-20T17:51:58.213 回答
3

@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%';

产量

+------+
| 编号 |
+------+
| 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%';

将产生:

+------+
| 编号 |
+------+
| 1 |
+------+

当然,OP 应该在应用程序(PHP)、连接(Linux 上的 MySQL 在 5.0 中默认为 latin1,但在 5.1 中默认为 UTF8)和字段数据类型中具有相同的字符集,以减少未知数。排序规则负责其余部分。

编辑:我写应该更好地控制一切,但以下也有效:

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 和members2latin1 中。

+--------+----------+------+----------+
| 编号 | 姓氏 | 编号 | 姓氏 |
+--------+----------+------+----------+
| 5 | 蕾妮 | 1 | 蕾妮 |
| 6 | 蕾妮 | 1 | 蕾妮 |
| 7 | 蕾妮 | 1 | 蕾妮 |
+--------+----------+------+----------+

这证明了正确的设置,排序规则为您完成了工作。

于 2010-12-25T06:31:28.090 回答