HASHBYTES返回varbinary类型(如0x开头的十六进制值),需用CONVERT(VARCHAR, ..., 2)转为字符串并SUBSTRING去前缀,参与哈希的NULL须用ISNULL处理,推荐SHA2_512加动态盐值,跨库比对需统一字节序列。

HASHBYTES 返回的是二进制,不是字符串
直接 SELECT HASHBYTES('SHA2_512', 'hello') 得到的是 0x... 开头的 VARBINARY 值,不能和 CHAR/VARCHAR 字段直接比对。很多校验失败就卡在这一步——比如你把哈希值存成 CHAR(128),但没转格式,插入时隐式转换可能截断或补空格,后续比对永远为假。
正确做法是统一转成大写十六进制字符串:
- SQL Server 用
CONVERT(VARCHAR(128), HASHBYTES('SHA2_512', @data), 2)→ 带0x前缀 - 要去掉前缀就用
SUBSTRING(CONVERT(VARCHAR(128), HASHBYTES('SHA2_512', @data), 2), 3, 128) - 务必加
COLLATE utf8mb4_bin或Latin1_General_BIN避免大小写不敏感导致abc和ABC被判等价
拼接字段时 NULL 会让整个 HASHBYTES 返回 NULL
比如 HASHBYTES('SHA2_256', CONCAT(@email, '|', @phone)),只要 @email 或 @phone 是 NULL,结果就是 NULL,不是你期望的“空字符串参与计算”。这在巡检脚本里极易漏掉差异。
安全拼接要显式处理 NULL:
- 用
ISNULL(@email, '')替代裸字段 - 或改用
CONCAT(ISNULL(@email,''), '|', ISNULL(@phone,'')) - 更严谨的:所有参与哈希的字段都走
CAST(... AS VARCHAR) + '|',避免 CONCAT 的 NULL 吞并逻辑
算法选 SHA2_512 而不是 MD5,但别硬编码盐值
MD5 已被证明易碰撞,HASHBYTES('MD5', ...) 只适合兼容旧系统或非安全场景。生产环境关键字段(如订单金额、用户证件号)必须用 SHA2_512,且必须加盐。
盐值不能写死,也不能全局复用:
- 每行独立生成:
CRYPT_GEN_RANDOM(16) - 盐值存在单独列(如
data_salt VARBINARY(16)),别和哈希值拼一起 - 校验时重新算:
HASHBYTES('SHA2_512', CONCAT(@data, @salt)),再转字符串比对
跨库比对时,字节序列必须一致
SQL Server 的 HASHBYTES 操作原始字节,而 MySQL 的 SHA2() 默认按连接字符集解释字符串。同一中文字段,在 UTF-8 和 UTF-16 下字节不同,哈希值必然不同。
强制统一字节序列:
- SQL Server 端:用
HASHBYTES('SHA2_256', CAST(@data AS BINARY)) - MySQL 端:用
SHA2(CONVERT(@data USING binary), 256) - 两边都确认字段定义为
utf8mb4,且 INSERT/UPDATE 时不带 BOM 或不可见控制字符
最容易被忽略的是空格和排序规则——哪怕只多一个尾部空格,哈希值就全错;哪怕只是 collation 不同,WHERE hash_col = @computed 就可能因隐式转换失效。校验逻辑上线前,务必用已知明文跑通端到端字节流。

















