登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

MySQL WEIGHT_STRING 怎样比较排序规则产生的权重

来源:17golang原创

时间:2026-10-29 15:42:59 492浏览 收藏

排查“为什么两个字符串在排序时看起来相等”时,直接把原文转成十六进制并不够,因为 MySQL 的字符比较会受字符集和排序规则影响。WEIGHT_STRING() 返回的不是原字符串,而是参与比较和排序的二进制权重;用 HEX() 包一层,就能把不可打印的二进制值变成便于对照的十六进制文本。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/string-functions.html

最稳妥的看法是:先显式固定字符集与排序规则,再用 HEX(WEIGHT_STRING(...)) 比较结果;如果输入本来就是 BINARY,看到的则是字节值,不是字符排序权重。

先把排序权重变成可读结果

WEIGHT_STRING(str) 返回二进制字符串。对于 CHAR、VARCHAR、TEXT 等非二进制字符输入,它包含当前排序规则用于比较的权重;对于 BINARY、VARBINARY、BLOB 输入,它按字节值处理。调试时建议始终配合 HEX():

-- 用固定字符集和排序规则观察字符权重;HEX 只负责把二进制结果显示成可读文本
SET @s = _utf8mb4 'AB' COLLATE utf8mb4_0900_ai_ci;
SELECT @s, HEX(@s), HEX(WEIGHT_STRING(@s));

-- 预期观察形式:原文的 HEX 与权重的 HEX 是两列不同含义
-- @s = AB,HEX(@s) = 4142,权重 HEX 类似 1C471C60

这里的关键不是记住某一串十六进制数字,而是区分两层:HEX(@s) 是字符编码后的字节表现,HEX(WEIGHT_STRING(@s)) 才是排序规则参与比较的权重表现。官方手册把该函数定位为内部调试用途,因此不应把具体权重编码当作业务协议。

固定排序规则后,权重到底表示什么

排序规则改变时,同一段文本可能得到不同权重;同一排序规则也可能让大小写不同的文本得到相同权重。例如在大小写不敏感的排序规则下,AB 与 ab 的权重可以相同,这正是排序时被视为等价的原因之一:

-- 两个表达式显式使用同一排序规则,便于对照比较权重
SET @upper = _utf8mb4 'AB' COLLATE utf8mb4_0900_ai_ci;
SET @lower = _utf8mb4 'ab' COLLATE utf8mb4_0900_ai_ci;

SELECT
  HEX(WEIGHT_STRING(@upper)) AS upper_weight,
  HEX(WEIGHT_STRING(@lower)) AS lower_weight,
  WEIGHT_STRING(@upper) = WEIGHT_STRING(@lower) AS same_weight;
-- 该结果用于解释排序规则;不要把权重字节直接保存为跨版本业务字段

按官方定义,如果两个权重相等,两个字符串在该比较语境下被视为相等;权重较小的字符串排在权重较大的字符串之前。注意,这个结论依赖输入携带的字符语义。只比较原始 UTF-8 字节,不能替代对排序规则的观察。

字符表达式经过字符集和排序规则进入 WEIGHT_STRING 并由 HEX 展示权重的静态结构图
图1:排序规则与权重输出的结构说明图,展示字符输入、比较权重和十六进制观察层的关系;这是静态说明图,不是运行截图。

字符字符串和二进制字符串不要混着看

同样写成 AB,如果显式转换成二进制字符串,WEIGHT_STRING() 返回的就是输入字节本身。下面两组结果的差异,说明“字符排序权重”和“字节值”是两条不同的排查路线:

-- 字符串走排序规则权重;二进制字符串按每个字节的数值返回
SET @text_value = _utf8mb4 'AB' COLLATE utf8mb4_0900_ai_ci;
SET @binary_value = CAST('AB' AS BINARY);

SELECT HEX(WEIGHT_STRING(@text_value)) AS collation_weight;
SELECT HEX(WEIGHT_STRING(@binary_value)) AS byte_weight;
-- 字符输入的结果由排序规则决定;BINARY 输入通常与 4142 这类原始字节表示一致

因此,遇到排序结果与预期不同,先确认列是字符类型还是二进制类型,再确认表达式有没有显式 COLLATE。不要看到十六进制结果就直接断言“编码错了”。

AS CHAR 与 AS BINARY 怎样改变长度边界

AS 子句可以把输入强制成指定类型和长度。AS CHAR(N) 按字符计数,右侧用空格补齐;AS BINARY(N) 按字节计数,右侧用 0x00 补齐。若输入超过长度,都会截断且不会因为这个截断产生警告:

-- 对比字符长度和字节长度的补齐规则;N 的单位随类型变化
SELECT HEX(WEIGHT_STRING('ab' AS CHAR(4))) AS char_weight;
SELECT HEX(WEIGHT_STRING('ab' AS BINARY(4))) AS binary_weight;
-- CHAR(4) 的示例会体现空格补齐;BINARY(4) 会体现 0x00 补齐

-- 过长输入按目标长度截断,示例只用于定位长度边界
SELECT HEX(WEIGHT_STRING('abcdef' AS CHAR(4))) AS truncated_char;
SELECT HEX(WEIGHT_STRING('abcdef' AS BINARY(4))) AS truncated_binary;

这个差异很适合解释固定宽度字段、导入比较或排序异常:字符类型的长度单位是字符,二进制类型的长度单位是字节。多字节字符场景下,不能把两者的 N 当成同一个尺度。

原始字符串分别进入 AS CHAR 和 AS BINARY 后产生空格或零字节填充的静态结构图
图2:AS CHAR 与 AS BINARY 的长度边界说明图,突出字符补空格、二进制补 0x00 及截断差异;这是静态说明图,不是运行截图。

实际排查时记住这三个边界

  • 先固定语境:同时记录字符集、排序规则、输入类型和是否显式转换。
  • 再看二进制:用 HEX(WEIGHT_STRING(...)) 观察,不要把终端的不可打印字符当成空结果。
  • 最后定用途:它适合测试和调试排序规则,不适合把具体权重字节当作跨 MySQL 版本稳定的业务格式。

还要留意 NULL:输入为 NULL 时函数返回 NULL;非空结果的类型可能是 VARBINARY,长度超过上限时则可能是 BLOB。这些信息能帮助你把“没有看到权重”和“权重是不可打印二进制”区分开。

相关问题

WEIGHT_STRING 能不能替代 ORDER BY?

不建议。它是调试排序规则的函数,官方明确提示行为可能随 MySQL 版本变化。业务排序应直接表达字符列、排序规则和稳定的次排序键。

为什么需要 HEX?

因为返回值是二进制字符串,可能包含不可打印字节;HEX 只是把它转换成便于复制和比较的十六进制文本。

AS CHAR 和 AS BINARY 的 N 有什么区别?

前者按字符计数并使用空格填充,后者按字节计数并使用 0x00 填充;多字节字符下,两者的长度边界不能混用。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>