HASHBYTES生成MD5需用CONVERT(...,2)转十六进制再SUBSTRING截取去0x前缀并LOWER转小写,标准写法为LOWER(SUBSTRING(CONVERT(VARCHAR(34), HASHBYTES('MD5',str),2),3,32));MD5已弃用,安全场景应改用SHA2_256。

HASHBYTES 是 SQL Server 中生成 MD5 哈希值的唯一内置函数,但它不直接返回 32 位十六进制字符串——这是绝大多数人踩坑的第一步。你看到的 0xE10ADC3949BA59ABBE56E057F20F883E 是 varbinary 类型的原始输出,不是标准 MD5 字符串。
为什么 CONVERT(VARCHAR, HASHBYTES(...), 2) 有时失效?
CONVERT 的第三个参数(样式)控制二进制转字符串格式:
- 样式
2:返回带0x前缀的十六进制字符串(如'0xE10ADC39...'),长度为 34 - 样式
1:返回带0x前缀、大写字符的字符串(同上,仅大小写差异) - 没有样式能直接去掉
0x并保证小写 32 位
常见错误是写成:
SELECT CONVERT(VARCHAR(32), HASHBYTES('MD5', '123456'), 2)
结果仍是 '0xE10ADC3949BA59ABBE56E057F20F883E' —— 超出 32 位,且含前缀。
正确做法是组合截取:
-
CONVERT(VARCHAR(34), HASHBYTES('MD5', '123456'), 2)得到完整带前缀字符串 - 再用
SUBSTRING(..., 3, 32)去掉前两位'0x'
所以标准写法是:
SELECT LOWER(SUBSTRING(CONVERT(VARCHAR(34), HASHBYTES('MD5', '123456'), 2), 3, 32))输出:e10adc3949ba59abbe56e057f20f883e
sys.fn_varbintohexstr 与 sys.fn_sqlvarbasetostr 的区别
这两个系统函数都能把 varbinary 转成十六进制字符串,但行为不同:
-
sys.fn_varbintohexstr:SQL Server 2005+ 支持,返回带0x前缀的大写字符串(如'0xE10ADC39...') -
sys.fn_sqlvarbasetostr:返回带0x前缀的小写字符串(如'0xe10adc39...'),更贴近通用 MD5 工具输出
两者都需手动截取:
-
SUBSTRING(sys.fn_varbintohexstr(HASHBYTES('MD5','123456')), 3, 32)→ 大写 32 位 -
LOWER(SUBSTRING(sys.fn_sqlvarbasetostr(HASHBYTES('MD5','123456')), 3, 32))→ 小写 32 位(推荐)
注意:sys.fn_varbintohexstr 在 Azure SQL 数据库中不可用;sys.fn_sqlvarbasetostr 在 SQL Server 2012+ 和 Azure SQL 中均可用。
MD5 在 SQL Server 中已过时,但还能用吗?
从 SQL Server 2016 (13.x) 起,'MD5' 算法已被官方标记为弃用,执行时会触发弃用事件(不影响运行,但日志里有警告)。微软明确建议改用 'SHA2_256' 或 'SHA2_512'。
如果你只是做兼容性校验(比如比对旧系统存的 MD5)、或内部非安全场景(如生成短标识、去重键),继续用 HASHBYTES('MD5', ...) 没问题。
但涉及密码存储、签名验证等安全用途,必须避免 MD5 —— 它早已被证明可碰撞,且无盐值保护,不满足现代安全基线。
替代方案示例(SHA2-256,64 位十六进制):
SELECT LOWER(SUBSTRING(CONVERT(VARCHAR(66), HASHBYTES('SHA2_256', '123456'), 2), 3, 64))在 INSERT/UPDATE 中安全使用 HASHBYTES 生成 MD5 字符串
直接把 HASHBYTES 结果插入 nvarchar 列会导致乱码(因为它是 varbinary),必须先转字符串。
常见错误写法:
INSERT INTO users (username, password_md5) VALUES ('alice', HASHBYTES('MD5', 'pwd'))→ password_md5 列存的是乱码二进制解释,不是可读字符串。
正确方式(显式转换 + 截取):
INSERT INTO users (username, password_md5)
SELECT 'alice', LOWER(SUBSTRING(CONVERT(VARCHAR(34), HASHBYTES('MD5', 'pwd'), 2), 3, 32))若频繁使用,建议封装为标量函数(注意性能):
CREATE FUNCTION dbo.MD5(@input VARCHAR(8000))
RETURNS VARCHAR(32)
AS BEGIN
RETURN LOWER(SUBSTRING(CONVERT(VARCHAR(34), HASHBYTES('MD5', @input), 2), 3, 32))
END然后调用:INSERT ... VALUES ('alice', dbo.MD5('pwd'))
注意:函数内不能用 PRINT,且 HASHBYTES 输入超过 8000 字节在 SQL Server 2014 及更早版本会截断。
实际使用中最容易被忽略的点是:所有转换逻辑必须在 SQL 层完成,不能依赖客户端再处理二进制输出——因为 varbinary 经 ODBC/JDBC 传输可能被隐式编码或截断,导致结果不一致。

















