我想知道,对于您不能100%确定将要输入的内容的普通网站,MySQL中是否有“最佳”排序选择?我知道所有的编码应该是相同的,比如MySQL、Apache、HTML和PHP中的任何内容。
在过去,我将PHP设置为以“UTF-8”输出,但这与MySQL中的排序规则匹配?我认为它是UTF-8之一,但我以前使用过utf8_unicode_ci、utf8_general_ci和utf8_bin。
我想知道,对于您不能100%确定将要输入的内容的普通网站,MySQL中是否有“最佳”排序选择?我知道所有的编码应该是相同的,比如MySQL、Apache、HTML和PHP中的任何内容。
在过去,我将PHP设置为以“UTF-8”输出,但这与MySQL中的排序规则匹配?我认为它是UTF-8之一,但我以前使用过utf8_unicode_ci、utf8_general_ci和utf8_bin。
当前回答
本质上,这取决于你如何看待字符串。
由于Guus强调的问题,我总是使用utf8_bin。在我看来,就数据库而言,字符串仍然只是一个字符串。字符串是一组UTF-8字符。一个字符有一个二进制表示,所以为什么它需要知道你使用的语言?通常,人们会为多语言站点的系统构建数据库。这就是使用UTF-8作为字符集的全部意义。我是一个有点纯粹的人,但我认为错误风险大大超过了索引可能带来的微小优势。任何与语言相关的规则都应该在比DBMS高得多的级别上执行。
在我的书中,“价值”在一百万年内永远不应该等于“价值”。
如果我想存储文本字段并进行不区分大小写的搜索,我将使用MYSQL字符串函数和PHP函数,如LOWER()和PHP函数strtolower()。
其他回答
非常非常注意使用utf8_general_ci时可能出现的问题。
当使用utf8_general_ci排序规则时,MySQL不会区分select语句中的某些字符。这可能会导致非常严重的错误,尤其是涉及用户名的错误。根据使用数据库表的实现,此问题可能允许恶意用户创建与管理员帐户匹配的用户名。
这个问题至少在5.x早期版本中会暴露出来——我不确定这种行为后来是否发生了变化。
我不是DBA,但为了避免这个问题,我总是使用utf8 bin,而不是不区分大小写的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;
对于UTF-8文本信息,应该使用utf8_general_ci,因为。。。
utf8_bin:按中每个字符的二进制值字符串utf8_general_ci:比较字符串使用通用语言规则和使用不区分大小写的比较
也就是说,它将使搜索和索引数据更快、更有效、更有用。
对于Guus强调的情况,我强烈建议使用utf8_unicode_cs(区分大小写,严格匹配,大多数情况下正确排序),而不是utf8_bin(严格匹配,不正确排序)。
如果要搜索字段,而不是匹配用户,则使用utf8_general_ci或utf8_unicode_ci。两者都不区分大小写,将失去匹配(“ß”等于“s”,而不是“ss”)。还有一些特定于语言的版本,如utf8_german_ci,其中丢失匹配更适合指定的语言。
[编辑-近6年后]
我不再推荐MySQL上的“utf8”字符集,而是推荐“utf8mb4”字符集。它们几乎完全匹配,但允许更多的unicode字符。
实际上,MySQL应该更新了“utf8”字符集和相应的排序规则,以匹配“utf7”规范,但取而代之的是,单独的字符集和各自的排序规则不会影响已使用其不完整的“utf9”字符集的存储指定。
本质上,这取决于你如何看待字符串。
由于Guus强调的问题,我总是使用utf8_bin。在我看来,就数据库而言,字符串仍然只是一个字符串。字符串是一组UTF-8字符。一个字符有一个二进制表示,所以为什么它需要知道你使用的语言?通常,人们会为多语言站点的系统构建数据库。这就是使用UTF-8作为字符集的全部意义。我是一个有点纯粹的人,但我认为错误风险大大超过了索引可能带来的微小优势。任何与语言相关的规则都应该在比DBMS高得多的级别上执行。
在我的书中,“价值”在一百万年内永远不应该等于“价值”。
如果我想存储文本字段并进行不区分大小写的搜索,我将使用MYSQL字符串函数和PHP函数,如LOWER()和PHP函数strtolower()。
公认的答案相当明确地建议使用utf8_unicode_ci,而对于很棒的新项目,我想讲述一下我最近的相反经验,以防节省任何人的时间。
因为utf8_general_ci是MySQL中Unicode的默认排序规则,所以如果您想使用utf8_Unicode_ci,那么您必须在很多地方指定它。
例如,所有客户端连接不仅有一个默认字符集(对我来说有意义),而且还有一个默认排序规则(即,对于unicode,排序规则将始终默认为utf8_general_ci)。
很可能,如果您对字段使用utf8_unicode_ci,则需要更新连接到数据库的脚本,以明确提及所需的排序规则,否则当您的连接使用默认排序规则时,使用文本字符串的查询可能会失败。
结果是,当将任何大小的现有系统转换为Unicode/utf8时,由于MySQL处理默认值的方式,您可能会被迫使用utf8_general_ci。