我想知道对于您不能 100% 确定将输入什么内容的一般网站,MySQL 中是否有排序规则的“最佳”选择?我知道所有编码都应该相同,例如 MySQL、Apache、HTML 和 PHP 中的任何内容。

过去我将 PHP 设置为以“UTF-8”输出,但是这与 MySQL 中的哪种排序规则匹配?我认为它是 UTF-8 之一,但我用过 utf8_unicode_ci, utf8_general_ci, , 和 utf8_bin 前。

有帮助吗?

解决方案

主要区别在于排序准确性(比较语言中的字符时)和性能。唯一特殊的是utf8_bin,它用于比较二进制格式的字符。

utf8_general_ciutf8_unicode_ci, ,但不太准确(用于排序)。这 特定语言utf8编码 (例如 utf8_swedish_ci) 包含其他语言规则,使它们能够最准确地对这些语言进行排序。大多数时候我使用 utf8_unicode_ci (我更喜欢准确性而不是小的性能改进),除非我有充分的理由更喜欢特定的语言。

您可以在 MySQL 手册上阅读有关特定 unicode 字符集的更多信息 - http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html

其他提示

要非常,非常注意这个问题,可以使用utf8_general_ci时发生的。

MySQL不会在所选择的语句一些字符之间进行区分,如果使用utf8_general_ci归类。这可能导致非常讨厌的错误 - 特别是例如,在用户名都参与其中。根据使用的数据库表的实现,这个问题可能允许恶意用户创建一个用户名相匹配的管理员帐户。

这个问题暴露本身至少是在早期5.x版本 - 我不知道这种行为,因为后来改为

我不是DBA,但要避免这个问题,我总是去utf8-bin,而不是一个不区分大小写的。

在脚本下面通过实施例描述了该问题。

-- first, create a sandbox to play in
CREATE DATABASE `sandbox`;
use `sandbox`;

-- next, make sure that your client connection is of the same 
-- character/collate type as the one we're going to test next:
charset utf8 collate utf8_general_ci

-- now, create the table and fill it with values
CREATE TABLE `test` (`key` VARCHAR(16), `value` VARCHAR(16) )
    CHARACTER SET utf8 COLLATE utf8_general_ci;

INSERT INTO `test` VALUES ('Key ONE', 'value'), ('Key TWO', 'valúe');

-- (verify)
SELECT * FROM `test`;

-- now, expose the problem/bug:
SELECT * FROM test WHERE `value` = 'value';

--
-- Note that we get BOTH keys here! MySQLs UTF8 collates that are 
-- case insensitive (ending with _ci) do not distinguish between 
-- both values!
--
-- collate 'utf8_bin' doesn't have this problem, as I'll show next:
--

-- first, reset the client connection charset/collate type
charset utf8 collate utf8_bin

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Note that we get just one key now, as you'd expect.
--
-- This problem appears to be specific to utf8. Next, I'll try to 
-- do the same with the 'latin1' charset:
--

-- first, reset the client connection charset/collate type
charset latin1 collate latin1_general_ci

-- next, convert the values that we've previously inserted
-- in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET latin1 COLLATE latin1_general_ci;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Again, only one key is returned (expected). This shows 
-- that the problem with utf8/utf8_generic_ci isn't present 
-- in latin1/latin1_general_ci
--
-- To complete the example, I'll check with the binary collate
-- of latin1 as well:

-- first, reset the client connection charset/collate type
charset latin1 collate latin1_bin

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET latin1 COLLATE latin1_bin;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Again, only one key is returned (expected).
--
-- Finally, I'll re-introduce the problem in the exact same 
-- way (for any sceptics out there):

-- first, reset the client connection charset/collate type
charset utf8 collate utf8_generic_ci

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

-- now, re-check for the problem/bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Two keys.
--

DROP DATABASE sandbox;

实际上,您可能想使用 utf8_unicode_ci 或者 utf8_general_ci.

  • utf8_general_ci 通过去除所有重音符号并像 ASCII 一样进行排序来进行排序
  • utf8_unicode_ci 使用 Unicode 排序顺序,因此可以在更多语言中正确排序

但是,如果您仅使用它来存储英文文本,那么它们应该没有什么不同。

最好使用字符集 utf8mb4 与整理 utf8mb4_unicode_ci.

字符集, utf8, ,仅支持少量 UTF-8 代码点,大约 6% 的可能字符。 utf8 仅支持基本多语言平面 (BMP)。还有其他16架飞机。每个平面包含 65,536 个字符。 utf8mb4 支持所有 17 个平面。

MySQL 将截断 4 字节 UTF-8 字符,从而导致数据损坏。

utf8mb4 字符集于2010年3月24日在MySQL 5.5.3中引入。

使用新字符集所需的一些更改并非微不足道:

  • 可能需要在您的应用程序数据库适配器中进行更改。
  • 需要对 my.cnf 进行更改,包括设置字符集、排序规则以及将 innodb_file_format 切换为 Barracuda
  • SQL CREATE 语句可能需要包括: ROW_FORMAT=DYNAMIC
    • VARCHAR(192) 及更大的索引需要 DYNAMIC。

笔记:切换到 BarracudaAntelope, ,可能需要多次重新启动 MySQL 服务。 innodb_file_format_max 直到 MySQL 服务重新启动后才会更改为: innodb_file_format = barracuda.

MySQL 使用旧的 Antelope InnoDB 文件格式。 Barracuda 支持动态行格式,如果您不想在切换到字符集后在创建索引和键时遇到 SQL 错误,则需要使用动态行格式: utf8mb4

  • #1709 - 索引列大小太大。最大列大小为 767 字节。
  • #1071 - 指定的密钥太长;最大密钥长度为 767 字节

以下场景已在 MySQL 5.6.17 上测试:默认情况下,MySQL 配置如下:

SHOW VARIABLES;

innodb_large_prefix = OFF
innodb_file_format = Antelope

停止 MySQL 服务并将选项添加到现有的 my.cnf 中:

[client]
default-character-set= utf8mb4

[mysqld]
explicit_defaults_for_timestamp = true
innodb_large_prefix = true
innodb_file_format = barracuda
innodb_file_format_max = barracuda
innodb_file_per_table = true

# Character collation
character_set_server=utf8mb4
collation_server=utf8mb4_unicode_ci

SQL CREATE 语句示例:

CREATE TABLE Contacts (
 id INT AUTO_INCREMENT NOT NULL,
 ownerId INT DEFAULT NULL,
 created timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 contact VARCHAR(640) NOT NULL,
 prefix VARCHAR(128) NOT NULL,
 first VARCHAR(128) NOT NULL,
 middle VARCHAR(128) NOT NULL,
 last VARCHAR(128) NOT NULL,
 suffix VARCHAR(128) NOT NULL,
 notes MEDIUMTEXT NOT NULL,
 INDEX IDX_CA367725E05EFD25 (ownerId),
 INDEX created (created),
 INDEX modified_idx (modified),
 INDEX contact_idx (contact),
 PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB ROW_FORMAT=DYNAMIC;
  • 您可以看到生成的错误#1709 INDEX contact_idx (contact) 如果 ROW_FORMAT=DYNAMIC 从 CREATE 语句中删除。

笔记:更改索引以限制为前 128 个字符 contact消除了使用 Barracuda 的要求 ROW_FORMAT=DYNAMIC

INDEX contact_idx (contact(128)),

另请注意:当它说字段的大小是 VARCHAR(128), ,这不是 128 字节。您可以使用 128 个 4 字节字符或 128 个 1 字节字符。

INSERT 语句应在第 2 行中包含 4 字节“poo”字符:

INSERT INTO `Contacts` (`id`, `ownerId`, `created`, `modified`, `contact`, `prefix`, `first`, `middle`, `last`, `suffix`, `notes`) VALUES
(1, NULL, '0000-00-00 00:00:00', '2014-08-25 03:00:36', '1234567890', '12345678901234567890', '1234567890123456789012345678901234567890', '1234567890123456789012345678901234567890', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678', '', ''),
(2, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '', ''),
(3, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '123💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '', '');

您可以看到所使用的空间量 last 柱子:

mysql> SELECT BIT_LENGTH(`last`), CHAR_LENGTH(`last`) FROM `Contacts`;
+--------------------+---------------------+
| BIT_LENGTH(`last`) | CHAR_LENGTH(`last`) |
+--------------------+---------------------+
|               1024 |                 128 | -- All characters are ASCII
|               4096 |                 128 | -- All characters are 4 bytes
|               4024 |                 128 | -- 3 characters are ASCII, 125 are 4 bytes
+--------------------+---------------------+

在数据库适配器中,您可能需要设置连接的字符集和排序规则:

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'

在 PHP 中,这将被设置为: \PDO::MYSQL_ATTR_INIT_COMMAND

参考:

从本质上讲,这取决于你是怎么想的字符串。

我总是使用,因为由希丁克突出的问题utf8_bin。在我看来,只要数据库应该关注的,一个字符串仍然只是一个字符串。的字符串是一个数字UTF-8字符的。一个字符的二进制表示为何还需要知道你使用的语言?通常情况下,人们将构建数据库与范围的多语言网站系统。这是使用UTF-8作为字符集的整点。我有点pureist的,但我认为错误的风险严重大于略占优势,你可能会得到索引。任何语言相关的规则应比DBMS一个更高的水平来实现。

在我的书“价值”不应该在一万年等于“值”。

如果我想存储的文本字段,并做不区分大小写的搜索,我将使用MYSQL字符串函数用PHP功能,例如LOWER()和PHP函数用strtolower()。

对于 UTF-8 文本信息,您应该使用 utf8_general_ci 因为...

  • utf8_bin: :通过字符串中每个字符的二进制值比较字符串

  • utf8_general_ci: :使用通用语言规则比较字符串,并使用对案例不敏感的比较

又名它将使数据的搜索和索引更快/更高效/更有用。

接受的答案非常明确地表明,使用utf8_unicode_ci,并同时为新项目这是伟大的,我想涉及我最近的经验,相反,以防万一它保存任何一段时间。

由于utf8_general_ci是在MySQL对Unicode的默认排序规则,如果你想使用utf8_unicode_ci,那么你最终不得不在很多的地方可以指定它。

例如,所有客户端连接不仅具有默认的字符集(有意义的我),还包括默认排序规则(即,核对将总是默认utf8_general_ci对Unicode)。

有可能的,如果你使用utf8_unicode_ci你的领域,你的脚本连接到数据库需要进行更新,以明确提及所需的整理 - 使用文本字符串时,你的连接使用默认排序规则可能无法以其他方式查询

其结果是,任何尺寸的现有系统转换为Unicode / UTF8时,你可能最终被迫使用utf8_general_ci的因为MySQL处理默认的方式。

有关由希丁克突出的情况下,我会强烈建议使用任一utf8_unicode_cs(区分大小写,严格匹配,正确排序的大部分)代替utf8_bin(严格匹配,不正确的排序)。

如果该字段的目的是要被搜索,而不是为用户匹配,然后使用utf8_general_ci或utf8_unicode_ci。两者都是不区分大小写,一个将losely匹配(“SS”等于“S”,而不是“SS”)。也有语言特定版本,例如utf8_german_ci其中失去匹配更适合于所指定的语言

[编辑 - 近6年后]

我不再推荐“UTF8”字符在MySQL设定,而不是推荐“utf8mb4”字符集。它们匹配几乎完全,但允许一小(很多)更多的Unicode字符。

实际上,MySQL的应该已经更新了“UTF8”字符集和相应的归类,以匹配“UTF8”说明书中,而是一个独立的字符集和相应的归类,以不为那些已经在使用他们的不完整的“UTF8影响存储指定“字符集。

我发现这些整理图表很有帮助。 http://collat​​ion-charts.org/mysql60/. 。我不确定哪个是使用的 utf8_general_ci 。

例如,这里是 utf8_swedish_ci 的图表。它显示它解释为相同的字符。 http://collat​​ion-charts.org/mysql60/mysql604.utf8_swedish_ci.html

在你的数据库上传文件,任何行之前添加以下行:

SET NAMES utf8;

和你的问题应该得到解决。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top