MySQL CHAR_LENGTH(str) 与不同的字符集引入器一起使用时,有时会为相同的字符串文字产生不同的输出

MySQL CHAR_LENGTH(str) sometimes produces different outputs for the same String literal when used with different character set introducers

这很奇怪。根据 MySQL 开发者网站 -

MySQL CHAR_LENGTH(str)

Returns the length of the string str, measured in characters. A multibyte character counts as a single character. This means that for a string containing five 2-byte characters, LENGTH() returns 10, whereas CHAR_LENGTH() returns 5.

很明显的意思是CHAR_LENGTH(str)的输出与字符集无关

现在如 -

Character String Literal Character Set and Collation

我可以使用 introducer 来设置字符串的字符集,如 -

SELECT 'abc';
SELECT _latin1'abc';
SELECT _binary'abc';
SELECT _utf8mb4'abc' COLLATE utf8mb4_danish_ci;

在我的例子中,我采用字符串文字“Hello”,使用介绍符将其设置为字符集,并将其用作 MySQL CHAR_LENGTH(str) 函数的参数。但奇怪的是,当使用不同的字符集时,它有时会产生不同的输出。示例 -

SELECT CHAR_LENGTH(_utf8mb4"Hello") AS character_length;
+------------------+
| character_length |
+------------------+
|                5 |
+------------------+

SELECT CHAR_LENGTH(_latin1"Hello") AS character_length;
+------------------+
| character_length |
+------------------+
|                5 |
+------------------+

SELECT CHAR_LENGTH(_ucs2"Hello") AS character_length;
+------------------+
| character_length |
+------------------+
|                3 |
+------------------+

在这里,“你好”有 5 个字符。字符集 _utf8mb4 和 _latin1 正确显示字符数。但是 CHARACTER SET _ucs2 奇怪地将数字 0f 字符显示为 3.

这是怎么回事?

那是因为显示的语言,这个词需要3个字符。

扩展我的回答。

字符转换在最好的情况下是困难的,应不惜一切代价避免。

首先MySQL尝试将 $Byte characte4r 转换为 ucs2 具有的 2 Byte 字符。

生成的字节随后用于显示字符,即您在代码段末尾看到的内容。

所以在字符集的转换中,你没有像计算机科学字节中到处都有的字母,它的表示形式可以说是 H,但是另一个字符集中的字节可以有另一种表示形式。此外,您始终需要一些规则,如果字节数不同,如何将一种字符集转换为另一种字符集。

所以我还扩展了示例,以向您展示确定性转换实际上发生了,如果您查看字节或二进制表示,您会发现所使用的具体算法。

SELECT CHAR_LENGTH(_ucs2"Hello") AS character_length;
| character_length |
| ---------------: |
|                3 |
SELECT _ucs2"Hello";
| 䡥汬  |
| :------ |
| H敬汯 |
SELECT _ucs2"Hel";
| 䡥  |
| :--- |
| H敬 |
SELECT _ucs2"Hell";
| 䡥汬 |
| :----- |
| 䡥汬 |
SELECT _ucs2"Hellos";
| 䡥汬潳 |
| :-------- |
| 䡥汬潳 |

db<>fiddle here

看起来像一个错误:

SELECT HEX(_ucs2"HELLO"), HEX(CONVERT("HELLO" USING ucs2));
+-------------------+----------------------------------+
| HEX(_ucs2"HELLO") | HEX(CONVERT("HELLO" USING ucs2)) |
+-------------------+----------------------------------+
| 0048454C4C4F      | 00480045004C004C004F             |
+-------------------+----------------------------------+

看来_ucs2介绍人只申请了第一个角色。

我创建了错误 https://bugs.mysql.com/bug.php?id=105394&thanks=4

来自错误报告

The _charset_name expression is formally called an introducer. It tells the parser, “the string that follows uses character set charset_name.” An introducer does not change the string to the introducer character set like CONVERT() would do. It does not change the string value, although padding may occur. The introducer is just a signal.