对每个查询使用不同的排序规则

Use different collation with each query

我有一个 table 叫 'names':

CREATE TABLE names (
    id              INT             UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    name            VARCHAR(255)    NOT NULL,
    sex             TINYINT         UNSIGNED,
    canon           BIT(1)          NOT NULL DEFAULT FALSE
  UNIQUE KEY uk_names (name, sex)
) ENGINE=InnoDB,
  CHARACTER SET utf8,
  COLLATE utf8_bin ;

里面有一堆法语名字。由于它们是法语,因此其中许多包含变音符号(é、à、è、ù、ï、ö、ü、â、ê、î、ô、û 和 ç)。因此,例如,Leia、Léia、Leïa 和 Léïa 这三个名字都被认为是不同的名字,并且在 MySQL 的眼中是独一无二的。

正是我的目的

但是,当我 运行 查询如(只是一个简化的示例)时:

SELECT * FROM names WHERE name = 'Leia'

我得到一行,对应于没有任何变音符号的准确拼写 'Leia'。

或者如果我 运行 这个查询:

SELECT * FROM names WHERE name LIKE '%Helene%'

我可能会得到名字 'Helene' 和 'Marie-Helene',但不会得到 'Hélène' 和 'Marie-Hélène'。

有时正是我想要的。有时我需要搜索一个准确的名称,并使用准确的拼写。但有时我想获得一个名字的所有变体拼写。

据我所知,这与我选择的排序规则 (utf8_bin) 有关。据我了解,如果我希望所有变体拼写都匹配,我需要 utf8_unicode_ci 来代替。对吗?

但是,如果我尝试将 table 更改为使用 utf8_unicode_ci,我会收到一条错误消息,指出由于我的唯一约束,存在重复行。这表明更改 table 本身的排序规则(并删除这样做的唯一约束)会给我带来相反的问题:无法查找名称的准确拼写。

那么如何为每个单独的查询即时选择排序规则?

我在查询之前尝试了 运行宁这个:

SET NAMES utf8 COLLATE utf8_unicode_ci

但这似乎没有任何作用,在我看到的关于 SET NAMES 的各种答案中,似乎建议您无论如何都不应该使用它(它会导致安全问题?)

顺便说一句,我使用 DSN mysql:host=...;dbname=...;charset=utf8 连接到 PHP 中的数据库(使用 PDO)以防万一。

我似乎能想到的唯一解决方案是创建一个名称的所有可能替代拼写列表(可能在 PHP 中),然后 运行 类似于:

SELECT * FROM names WHERE name IN ('Leia', 'Léia', 'Leïa', 'Léïa')

但由于有相当多的变音符号,它似乎可能会变成一长串包含某些名字的列表(Francois, François, Fràncois, Frànçois, Francoîs, Françoîs, Fràncoîs, Frànçoîs, François, Françöîs, . .. 等等。我知道其中一些拼写实际上并不存在,但如果不针对数据库测试所有可能性,我的脚本将无法知道哪些拼写。

肯定有更简单、更优雅的方法来做我想做的事吧?

您必须使用此查询:

SELECT * FROM names WHERE name LIKE _utf8'%Leia%' collate utf8_general_ci;

如果您使用的是 MySQL 5.7 或更早版本,utf8_unicode_520_ci 在某些情况下可能更好。

对于 8.0,utf8mb4_0900_ai_ci 是首选。

“_ci”表示不区分大小写。

"_as_ci" 表示区分重音和不区分大小写,与 8.0 的 utf8mb4_0900_as_ci .

“_bin”表示只比较位——所有重音和大小写都不同。