请帮助我了解如何在MySQL utf8mb4字段中处理像emoji表情这样的多字节字符。
请参阅下面的简单测试SQL来说明挑战。
/* Clear Previous Test */ DROP TABLE IF EXISTS `emoji_test`; DROP TABLE IF EXISTS `emoji_test_with_unique_key`; /* Build Schema */ CREATE TABLE `emoji_test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `string` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `status` tinyint(1) NOT NULL DEFAULT '1', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `emoji_test_with_unique_key` ( `id` int(11) NOT NULL AUTO_INCREMENT, `string` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `status` tinyint(1) NOT NULL DEFAULT '1', PRIMARY KEY (`id`), UNIQUE KEY `idx_string_status` (`string`,`status`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /* INSERT data */ # Expected Result is successful insert for each of these. # However some fail. See comments. INSERT INTO emoji_test (`string`, `status`) VALUES ('🌶', 1); # SUCCESS INSERT INTO emoji_test (`string`, `status`) VALUES ('🌮', 1); # SUCCESS INSERT INTO emoji_test (`string`, `status`) VALUES ('🌮🌶', 1); # SUCCESS INSERT INTO emoji_test (`string`, `status`) VALUES ('🌶🌮', 1); # SUCCESS INSERT INTO emoji_test_with_unique_key (`string`, `status`) VALUES ('🌶', 1); # SUCCESS INSERT INTO emoji_test_with_unique_key (`string`, `status`) VALUES ('🌮', 1); # FAIL: Duplicate entry '?-1' for key 'idx_string_status' INSERT INTO emoji_test_with_unique_key (`string`, `status`) VALUES ('🌮🌶', 1); # SUCCESS INSERT INTO emoji_test_with_unique_key (`string`, `status`) VALUES ('🌶🌮', 1); # FAIL: Duplicate entry '??-1' for key 'idx_string_status' /* Test data */ /* Simple Table */ SELECT * FROM emoji_test WHERE `string` IN ('🌶','🌮','🌮🌶','🌶🌮'); # SUCCESS (all 4 are found) SELECT * FROM emoji_test WHERE `string` IN ('🌶'); # FAIL: Returns both 🌶 and 🌮 SELECT * FROM emoji_test WHERE `string` IN ('🌮'); # FAIL: Returns both 🌶 and 🌮 SELECT * FROM emoji_test; # SUCCESS (all 4 are found) /* Table with Unique Key */ SELECT * FROM emoji_test_with_unique_key WHERE `string` IN ('🌶','🌮','🌮🌶','🌶🌮'); # FAIL: Only 2 are found (due to insert errors above) SELECT * FROM emoji_test_with_unique_key WHERE `string` IN ('🌶'); # SUCCESS SELECT * FROM emoji_test_with_unique_key WHERE `string` IN ('🌮'); # FAIL: 🌶 found instead of 🌮 SELECT * FROM emoji_test_with_unique_key;
我对了解导致上述FAILs的原因以及如何解决此问题很感兴趣。
FAIL
具体来说:
?
CREATE TABLE
您使用 utf8mb4_unicode_ci列,因此检查不区分大小写。如果utf8mb4_bin改用,则表情符号馃尞和correctly正确地标识为不同的字母。
utf8mb4_unicode_ci
utf8mb4_bin
使用,WEIGHT_STRING您可以获取用于输入字符串的排序和比较的值。
WEIGHT_STRING
如果您写:
SELECT WEIGHT_STRING ('馃尞' COLLATE 'utf8mb4_unicode_ci'), WEIGHT_STRING ('馃尪' COLLATE 'utf8mb4_unicode_ci')
然后您会看到两者都是0xfffd。在Unicode字符集中,他们说:
0xfffd
对于一般归类中的补充字符,权重是0xfffd替换字符的权重。
SELECT WEIGHT_STRING('🌮' COLLATE 'utf8mb4_bin'), WEIGHT_STRING('🌶' COLLATE 'utf8mb4_bin')
你会得到他们的Unicode值0x01f32e和0x01f336替代。
0x01f32e
0x01f336
对于其他的字母一样Ä,Á并且A如果您使用是相等的utf8mb4_unicode_ci,差异可以看出:
Ä
Á
A
SELECT WEIGHT_STRING ('Ä' COLLATE 'utf8mb4_unicode_ci'), WEIGHT_STRING ('A' COLLATE 'utf8mb4_unicode_ci')
那些映射到重量 0x0E33
0x0E33
Ä: 00C4 ; [.0E33.0020.0008.0041][.0000.0047.0002.0308] # LATIN CAPITAL LETTER A WITH DIAERESIS; QQCM A: 0041 ; [.0E33.0020.0008.0041] # LATIN CAPITAL LETTER A
根据:MariaDB /MySQL中utf8mb4_unicode_ci和utf8mb4_unicode_520_ci归类之间的区别?所使用的权重utf8mb4_unicode_ci基于UCA4.0.0,因为表情符号未出现在其中,映射的权重为0xfffd
如果您需要不区分大小写的比较以及对常规字母和表情符号进行排序,则可以使用以下方法解决此问题utf8mb4_unicode_520_ci:
utf8mb4_unicode_520_ci
SELECT WEIGHT_STRING('🌮' COLLATE 'utf8mb4_unicode_520_ci'), WEIGHT_STRING('🌶' COLLATE 'utf8mb4_unicode_520_ci')
这些表情符号0xfbc3f32e和也会有不同的权重0xfbc3f336。
0xfbc3f32e
0xfbc3f336